mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-23 04:03:00 -05:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0366e48fad | ||
|
|
b13317d32f | ||
|
|
57b1440a10 | ||
|
|
fc53356a1d | ||
|
|
5be99456f0 | ||
|
|
7adedcc704 |
@@ -25,16 +25,16 @@ repos:
|
|||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 24.10.0
|
rev: 25.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args: ["-l", "88", "--skip-string-normalization"]
|
args: ["-l", "88", "--skip-string-normalization"]
|
||||||
|
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.3.0
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell # See 'pyproject.toml' for args
|
- id: codespell # See 'pyproject.toml' for args
|
||||||
exclude: \.js\.map$
|
exclude: fs\.py|\.js\.map$
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- tomli
|
- tomli
|
||||||
|
|
||||||
|
|||||||
636
core/package-lock.json
generated
636
core/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.6.22",
|
"version": "0.6.30",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "PyScript",
|
"description": "PyScript",
|
||||||
"module": "./index.js",
|
"module": "./index.js",
|
||||||
@@ -25,6 +25,10 @@
|
|||||||
"types": "./types/core.d.ts",
|
"types": "./types/core.d.ts",
|
||||||
"import": "./src/core.js"
|
"import": "./src/core.js"
|
||||||
},
|
},
|
||||||
|
"./js": {
|
||||||
|
"types": "./types/core.d.ts",
|
||||||
|
"import": "./dist/core.js"
|
||||||
|
},
|
||||||
"./css": {
|
"./css": {
|
||||||
"import": "./dist/core.css"
|
"import": "./dist/core.css"
|
||||||
},
|
},
|
||||||
@@ -62,38 +66,38 @@
|
|||||||
"@webreflection/idb-map": "^0.3.2",
|
"@webreflection/idb-map": "^0.3.2",
|
||||||
"add-promise-listener": "^0.1.3",
|
"add-promise-listener": "^0.1.3",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"polyscript": "^0.16.10",
|
"polyscript": "^0.16.13",
|
||||||
"sabayon": "^0.6.1",
|
"sabayon": "^0.6.6",
|
||||||
"sticky-module": "^0.1.1",
|
"sticky-module": "^0.1.1",
|
||||||
"to-json-callback": "^0.1.1",
|
"to-json-callback": "^0.1.1",
|
||||||
"type-checked-collections": "^0.1.7"
|
"type-checked-collections": "^0.1.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/commands": "^6.7.1",
|
"@codemirror/commands": "^6.8.0",
|
||||||
"@codemirror/lang-python": "^6.1.6",
|
"@codemirror/lang-python": "^6.1.7",
|
||||||
"@codemirror/language": "^6.10.6",
|
"@codemirror/language": "^6.10.8",
|
||||||
"@codemirror/state": "^6.4.1",
|
"@codemirror/state": "^6.5.2",
|
||||||
"@codemirror/view": "^6.35.0",
|
"@codemirror/view": "^6.36.2",
|
||||||
"@playwright/test": "1.45.3",
|
"@playwright/test": "1.45.3",
|
||||||
"@rollup/plugin-commonjs": "^28.0.1",
|
"@rollup/plugin-commonjs": "^28.0.2",
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"@webreflection/toml-j0.4": "^1.1.3",
|
"@webreflection/toml-j0.4": "^1.1.3",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/addon-web-links": "^0.11.0",
|
"@xterm/addon-web-links": "^0.11.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"bun": "^1.1.38",
|
"bun": "^1.2.2",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.3",
|
||||||
"codedent": "^0.1.2",
|
"codedent": "^0.1.2",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"eslint": "^9.16.0",
|
"eslint": "^9.20.1",
|
||||||
"flatted": "^3.3.2",
|
"flatted": "^3.3.2",
|
||||||
"rollup": "^4.28.1",
|
"rollup": "^4.34.7",
|
||||||
"rollup-plugin-postcss": "^4.0.2",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-string": "^3.0.0",
|
"rollup-plugin-string": "^3.0.0",
|
||||||
"static-handler": "^0.5.3",
|
"static-handler": "^0.5.3",
|
||||||
"string-width": "^7.2.0",
|
"string-width": "^7.2.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.3",
|
||||||
"xterm-readline": "^1.1.2"
|
"xterm-readline": "^1.1.2"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const badURL = (url, expected = "") => {
|
|||||||
* @param {string?} type the optional type to enforce
|
* @param {string?} type the optional type to enforce
|
||||||
* @returns {{json: boolean, toml: boolean, text: string}}
|
* @returns {{json: boolean, toml: boolean, text: string}}
|
||||||
*/
|
*/
|
||||||
const configDetails = async (config, type) => {
|
export const configDetails = async (config, type) => {
|
||||||
let text = config?.trim();
|
let text = config?.trim();
|
||||||
// we only support an object as root config
|
// we only support an object as root config
|
||||||
let url = "",
|
let url = "",
|
||||||
|
|||||||
@@ -78,3 +78,13 @@ py-terminal span,
|
|||||||
mpy-terminal span {
|
mpy-terminal span {
|
||||||
letter-spacing: 0 !important;
|
letter-spacing: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog.pyscript-fs {
|
||||||
|
border-radius: 8px;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.pyscript-fs > div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
createFunction,
|
createFunction,
|
||||||
inputFailure,
|
inputFailure,
|
||||||
} from "./hooks.js";
|
} from "./hooks.js";
|
||||||
|
import * as fs from "./fs.js";
|
||||||
|
|
||||||
import codemirror from "./plugins/codemirror.js";
|
import codemirror from "./plugins/codemirror.js";
|
||||||
export { codemirror };
|
export { codemirror };
|
||||||
@@ -167,6 +168,8 @@ for (const [TYPE, interpreter] of TYPES) {
|
|||||||
// enrich the Python env with some JS utility for main
|
// enrich the Python env with some JS utility for main
|
||||||
interpreter.registerJsModule("_pyscript", {
|
interpreter.registerJsModule("_pyscript", {
|
||||||
PyWorker,
|
PyWorker,
|
||||||
|
fs,
|
||||||
|
interpreter,
|
||||||
js_import: (...urls) => Promise.all(urls.map((url) => import(url))),
|
js_import: (...urls) => Promise.all(urls.map((url) => import(url))),
|
||||||
get target() {
|
get target() {
|
||||||
return isScript(currentElement)
|
return isScript(currentElement)
|
||||||
@@ -223,13 +226,6 @@ for (const [TYPE, interpreter] of TYPES) {
|
|||||||
else element.after(show);
|
else element.after(show);
|
||||||
}
|
}
|
||||||
if (!show.id) show.id = getID();
|
if (!show.id) show.id = getID();
|
||||||
if (TYPE === "py") {
|
|
||||||
const canvas2D = element.getAttribute("canvas2d") || element.getAttribute("canvas");
|
|
||||||
if (canvas2D) {
|
|
||||||
const canvas = queryTarget(document, canvas2D);
|
|
||||||
wrap.interpreter.canvas.setCanvas2D(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// allows the code to retrieve the target element via
|
// allows the code to retrieve the target element via
|
||||||
// document.currentScript.target if needed
|
// document.currentScript.target if needed
|
||||||
|
|||||||
81
core/src/fs.js
Normal file
81
core/src/fs.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import IDBMap from "@webreflection/idb-map";
|
||||||
|
import { assign } from "polyscript/exports";
|
||||||
|
import { $$ } from "basic-devtools";
|
||||||
|
|
||||||
|
const stop = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ⚠️ these two constants MUST be passed as `fs`
|
||||||
|
// within the worker onBeforeRunAsync hook!
|
||||||
|
export const NAMESPACE = "@pyscript.fs";
|
||||||
|
export const ERROR = "storage permissions not granted";
|
||||||
|
|
||||||
|
export const idb = new IDBMap(NAMESPACE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask a user action via dialog and returns the directory handler once granted.
|
||||||
|
* @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options
|
||||||
|
* @returns {Promise<FileSystemDirectoryHandle>}
|
||||||
|
*/
|
||||||
|
export const getFileSystemDirectoryHandle = async (options) => {
|
||||||
|
if (!("showDirectoryPicker" in globalThis)) {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error("showDirectoryPicker is not supported"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { promise, resolve, reject } = Promise.withResolvers();
|
||||||
|
|
||||||
|
const how = { id: "pyscript", mode: "readwrite", ...options };
|
||||||
|
if (options.hint) how.startIn = options.hint;
|
||||||
|
|
||||||
|
const transient = async () => {
|
||||||
|
try {
|
||||||
|
/* eslint-disable */
|
||||||
|
const handler = await showDirectoryPicker(how);
|
||||||
|
/* eslint-enable */
|
||||||
|
if ((await handler.requestPermission(how)) === "granted") {
|
||||||
|
resolve(handler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch ({ message }) {
|
||||||
|
console.warn(message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// in case the user decided to attach the event itself
|
||||||
|
// as opposite of relying our dialog walkthrough
|
||||||
|
if (navigator.userActivation?.isActive) {
|
||||||
|
if (!(await transient())) reject(new Error(ERROR));
|
||||||
|
} else {
|
||||||
|
const dialog = assign(document.createElement("dialog"), {
|
||||||
|
className: "pyscript-fs",
|
||||||
|
innerHTML: [
|
||||||
|
"<strong>ℹ️ Persistent FileSystem</strong><hr>",
|
||||||
|
"<p><small>PyScript would like to access a local folder.</small></p>",
|
||||||
|
"<div><button title='ok'>✅ Authorize</button>",
|
||||||
|
"<button title='cancel'>❌</button></div>",
|
||||||
|
].join(""),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [ok, cancel] = $$("button", dialog);
|
||||||
|
|
||||||
|
ok.addEventListener("click", async (event) => {
|
||||||
|
stop(event);
|
||||||
|
if (await transient()) dialog.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
cancel.addEventListener("click", async (event) => {
|
||||||
|
stop(event);
|
||||||
|
reject(new Error(ERROR));
|
||||||
|
dialog.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(dialog).showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
@@ -84,23 +84,23 @@ export const hooks = {
|
|||||||
},
|
},
|
||||||
worker: {
|
worker: {
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onReady: new SetFunction([
|
onReady: new SetFunction(),
|
||||||
(wrap, xworker) => {
|
|
||||||
if (wrap.type === "py") {
|
|
||||||
const { interpreter } = wrap;
|
|
||||||
const element = wrap.run('from polyscript import currentScript;currentScript');
|
|
||||||
const canvas2D = element.getAttribute("canvas2d") || element.getAttribute("canvas");
|
|
||||||
if (canvas2D) {
|
|
||||||
const canvas = element.ownerDocument.getElementById(canvas2D);
|
|
||||||
interpreter.canvas.setCanvas2D(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]),
|
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onBeforeRun: new SetFunction(),
|
onBeforeRun: new SetFunction(),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onBeforeRunAsync: new SetFunction(),
|
onBeforeRunAsync: new SetFunction([
|
||||||
|
({ interpreter }) => {
|
||||||
|
interpreter.registerJsModule("_pyscript", {
|
||||||
|
// cannot be imported from fs.js
|
||||||
|
// because this code is stringified
|
||||||
|
fs: {
|
||||||
|
ERROR: "storage permissions not granted",
|
||||||
|
NAMESPACE: "@pyscript.fs",
|
||||||
|
},
|
||||||
|
interpreter,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
]),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
onAfterRun: new SetFunction(),
|
onAfterRun: new SetFunction(),
|
||||||
/** @type {Set<function>} */
|
/** @type {Set<function>} */
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ export default {
|
|||||||
/* webpackIgnore: true */
|
/* webpackIgnore: true */
|
||||||
"./plugins/py-editor.js"
|
"./plugins/py-editor.js"
|
||||||
),
|
),
|
||||||
|
["py-game"]: () =>
|
||||||
|
import(
|
||||||
|
/* webpackIgnore: true */
|
||||||
|
"./plugins/py-game.js"
|
||||||
|
),
|
||||||
["py-terminal"]: () =>
|
["py-terminal"]: () =>
|
||||||
import(
|
import(
|
||||||
/* webpackIgnore: true */
|
/* webpackIgnore: true */
|
||||||
|
|||||||
@@ -5,7 +5,13 @@ const { stringify } = JSON;
|
|||||||
|
|
||||||
const invoke = (name, args) => `${name}(code, ${args.join(", ")})`;
|
const invoke = (name, args) => `${name}(code, ${args.join(", ")})`;
|
||||||
|
|
||||||
const donkey = ({ type = "py", persistent, terminal, config }) => {
|
const donkey = ({
|
||||||
|
type = "py",
|
||||||
|
persistent,
|
||||||
|
terminal,
|
||||||
|
config,
|
||||||
|
serviceWorker,
|
||||||
|
}) => {
|
||||||
const globals = terminal ? '{"__terminal__":__terminal__}' : "{}";
|
const globals = terminal ? '{"__terminal__":__terminal__}' : "{}";
|
||||||
const args = persistent ? ["globals()", "__locals__"] : [globals, "{}"];
|
const args = persistent ? ["globals()", "__locals__"] : [globals, "{}"];
|
||||||
|
|
||||||
@@ -46,6 +52,7 @@ const donkey = ({ type = "py", persistent, terminal, config }) => {
|
|||||||
typeof config === "string" ? config : stringify(config),
|
typeof config === "string" ? config : stringify(config),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (serviceWorker) script.setAttribute("service-worker", serviceWorker);
|
||||||
|
|
||||||
return addPromiseListener(
|
return addPromiseListener(
|
||||||
document.body.appendChild(script),
|
document.body.appendChild(script),
|
||||||
|
|||||||
68
core/src/plugins/py-game.js
Normal file
68
core/src/plugins/py-game.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { dedent, define } from "polyscript/exports";
|
||||||
|
|
||||||
|
import { stdlib } from "../core.js";
|
||||||
|
import { configDetails } from "../config.js";
|
||||||
|
import { getText } from "../fetch.js";
|
||||||
|
|
||||||
|
let toBeWarned = true;
|
||||||
|
|
||||||
|
const hooks = {
|
||||||
|
main: {
|
||||||
|
onReady: async (wrap, script) => {
|
||||||
|
if (toBeWarned) {
|
||||||
|
toBeWarned = false;
|
||||||
|
console.warn("⚠️ EXPERIMENTAL `py-game` FEATURE");
|
||||||
|
}
|
||||||
|
if (script.hasAttribute("config")) {
|
||||||
|
const value = script.getAttribute("config");
|
||||||
|
const { json, toml, text } = configDetails(value);
|
||||||
|
let config = {};
|
||||||
|
if (json) config = JSON.parse(text);
|
||||||
|
else if (toml) {
|
||||||
|
const { parse } = await import(
|
||||||
|
/* webpackIgnore: true */ "../3rd-party/toml.js"
|
||||||
|
);
|
||||||
|
config = parse(text);
|
||||||
|
}
|
||||||
|
if (config.packages) {
|
||||||
|
const micropip = wrap.interpreter.pyimport("micropip");
|
||||||
|
await micropip.install(config.packages, {
|
||||||
|
keep_going: true,
|
||||||
|
});
|
||||||
|
micropip.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap.interpreter.registerJsModule("_pyscript", {
|
||||||
|
PyWorker() {
|
||||||
|
throw new Error(
|
||||||
|
"Unable to use PyWorker in py-game scripts",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
js_import: (...urls) =>
|
||||||
|
Promise.all(urls.map((url) => import(url))),
|
||||||
|
get target() {
|
||||||
|
return script.id;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrap.interpreter.runPythonAsync(stdlib);
|
||||||
|
|
||||||
|
let code = dedent(script.textContent);
|
||||||
|
if (script.src) code = await fetch(script.src).then(getText);
|
||||||
|
|
||||||
|
const target = script.getAttribute("target") || "canvas";
|
||||||
|
const canvas = document.getElementById(target);
|
||||||
|
wrap.interpreter.canvas.setCanvas2D(canvas);
|
||||||
|
await wrap.interpreter.runPythonAsync(code);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
define("py-game", {
|
||||||
|
config: { packages: ["pygame-ce"] },
|
||||||
|
configURL: new URL("./config.txt", location.href).href,
|
||||||
|
interpreter: "pyodide",
|
||||||
|
env: "py-game",
|
||||||
|
hooks,
|
||||||
|
});
|
||||||
@@ -7,7 +7,8 @@ export default {
|
|||||||
"fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A",
|
"fetch.py": "import json,js\nfrom pyscript.util import as_bytearray\nclass _Response:\n\tdef __init__(A,response):A._response=response\n\tdef __getattr__(A,attr):return getattr(A._response,attr)\n\tasync def arrayBuffer(B):\n\t\tA=await B._response.arrayBuffer()\n\t\tif hasattr(A,'to_py'):return A.to_py()\n\t\treturn memoryview(as_bytearray(A))\n\tasync def blob(A):return await A._response.blob()\n\tasync def bytearray(A):B=await A._response.arrayBuffer();return as_bytearray(B)\n\tasync def json(A):return json.loads(await A.text())\n\tasync def text(A):return await A._response.text()\nclass _DirectResponse:\n\t@staticmethod\n\tdef setup(promise,response):A=promise;A._response=_Response(response);return A._response\n\tdef __init__(B,promise):A=promise;B._promise=A;A._response=None;A.arrayBuffer=B.arrayBuffer;A.blob=B.blob;A.bytearray=B.bytearray;A.json=B.json;A.text=B.text\n\tasync def _response(A):\n\t\tif not A._promise._response:await A._promise\n\t\treturn A._promise._response\n\tasync def arrayBuffer(A):B=await A._response();return await B.arrayBuffer()\n\tasync def blob(A):B=await A._response();return await B.blob()\n\tasync def bytearray(A):B=await A._response();return await B.bytearray()\n\tasync def json(A):B=await A._response();return await B.json()\n\tasync def text(A):B=await A._response();return await B.text()\ndef fetch(url,**B):C=js.JSON.parse(json.dumps(B));D=lambda response,*B:_DirectResponse.setup(A,response);A=js.fetch(url,C).then(D);_DirectResponse(A);return A",
|
||||||
"ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs",
|
"ffi.py": "try:\n\timport js;from pyodide.ffi import create_proxy as _cp,to_js as _py_tjs;from_entries=js.Object.fromEntries\n\tdef _tjs(value,**A):\n\t\tB='dict_converter'\n\t\tif not hasattr(A,B):A[B]=from_entries\n\t\treturn _py_tjs(value,**A)\nexcept:from jsffi import create_proxy as _cp;from jsffi import to_js as _tjs\ncreate_proxy=_cp\nto_js=_tjs",
|
||||||
"flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)",
|
"flatted.py": "import json as _json\nclass _Known:\n\tdef __init__(A):A.key=[];A.value=[]\nclass _String:\n\tdef __init__(A,value):A.value=value\ndef _array_keys(value):\n\tA=[];B=0\n\tfor C in value:A.append(B);B+=1\n\treturn A\ndef _object_keys(value):\n\tA=[]\n\tfor B in value:A.append(B)\n\treturn A\ndef _is_array(value):A=value;return isinstance(A,list)or isinstance(A,tuple)\ndef _is_object(value):return isinstance(value,dict)\ndef _is_string(value):return isinstance(value,str)\ndef _index(known,input,value):B=value;A=known;input.append(B);C=str(len(input)-1);A.key.append(B);A.value.append(C);return C\ndef _loop(keys,input,known,output):\n\tA=output\n\tfor B in keys:\n\t\tC=A[B]\n\t\tif isinstance(C,_String):_ref(B,input[int(C.value)],input,known,A)\n\treturn A\ndef _ref(key,value,input,known,output):\n\tB=known;A=value\n\tif _is_array(A)and not A in B:B.append(A);A=_loop(_array_keys(A),input,B,A)\n\telif _is_object(A)and not A in B:B.append(A);A=_loop(_object_keys(A),input,B,A)\n\toutput[key]=A\ndef _relate(known,input,value):\n\tB=known;A=value\n\tif _is_string(A)or _is_array(A)or _is_object(A):\n\t\ttry:return B.value[B.key.index(A)]\n\t\texcept:return _index(B,input,A)\n\treturn A\ndef _transform(known,input,value):\n\tB=known;A=value\n\tif _is_array(A):\n\t\tC=[]\n\t\tfor F in A:C.append(_relate(B,input,F))\n\t\treturn C\n\tif _is_object(A):\n\t\tD={}\n\t\tfor E in A:D[E]=_relate(B,input,A[E])\n\t\treturn D\n\treturn A\ndef _wrap(value):\n\tA=value\n\tif _is_string(A):return _String(A)\n\tif _is_array(A):\n\t\tB=0\n\t\tfor D in A:A[B]=_wrap(D);B+=1\n\telif _is_object(A):\n\t\tfor C in A:A[C]=_wrap(A[C])\n\treturn A\ndef parse(value,*C,**D):\n\tA=value;E=_json.loads(A,*C,**D);B=[]\n\tfor A in E:B.append(_wrap(A))\n\tinput=[]\n\tfor A in B:\n\t\tif isinstance(A,_String):input.append(A.value)\n\t\telse:input.append(A)\n\tA=input[0]\n\tif _is_array(A):return _loop(_array_keys(A),input,[A],A)\n\tif _is_object(A):return _loop(_object_keys(A),input,[A],A)\n\treturn A\ndef stringify(value,*D,**E):\n\tB=_Known();input=[];C=[];A=int(_index(B,input,value))\n\twhile A<len(input):C.append(_transform(B,input,input[A]));A+=1\n\treturn _json.dumps(C,*D,**E)",
|
||||||
"magic_js.py": "import json,sys,js as globalThis\nfrom polyscript import config as _config,js_modules\nfrom pyscript.util import NotSupported\nRUNNING_IN_WORKER=not hasattr(globalThis,'document')\nconfig=json.loads(globalThis.JSON.stringify(_config))\nif'MicroPython'in sys.version:config['type']='mpy'\nelse:config['type']='py'\nclass JSModule:\n\tdef __init__(A,name):A.name=name\n\tdef __getattr__(B,field):\n\t\tA=field\n\t\tif not A.startswith('_'):return getattr(getattr(js_modules,B.name),A)\nfor name in globalThis.Reflect.ownKeys(js_modules):sys.modules[f\"pyscript.js_modules.{name}\"]=JSModule(name)\nsys.modules['pyscript.js_modules']=js_modules\nif RUNNING_IN_WORKER:\n\timport polyscript;PyWorker=NotSupported('pyscript.PyWorker','pyscript.PyWorker works only when running in the main thread')\n\ttry:import js;window=polyscript.xworker.window;document=window.document;js.screen=window.screen;js.document=document;js_import=window.Function('return (...urls) => Promise.all(urls.map((url) => import(url)))')()\n\texcept:message='Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer';globalThis.console.warn(message);window=NotSupported('pyscript.window',message);document=NotSupported('pyscript.document',message);js_import=None\n\tsync=polyscript.xworker.sync\n\tdef current_target():return polyscript.target\nelse:\n\timport _pyscript;from _pyscript import PyWorker,js_import;window=globalThis;document=globalThis.document;sync=NotSupported('pyscript.sync','pyscript.sync works only when running in a worker')\n\tdef current_target():return _pyscript.target",
|
"fs.py": "mounted={}\nasync def mount(path,mode='readwrite',root='',id='pyscript'):\n\tE=path;import js;from _pyscript import fs as A,interpreter as I;from pyscript.ffi import to_js as H;from pyscript.magic_js import RUNNING_IN_WORKER as J,sync;js.console.warn('experimental pyscript.fs ⚠️');B=None;C=f\"{E}@{id}\";F={'id':id,'mode':mode}\n\tif root!='':F['startIn']=root\n\tif J:\n\t\tG=sync.storeFSHandler(C,H(F))\n\t\tif isinstance(G,bool):D=G\n\t\telse:D=await G\n\t\tif D:from polyscript import IDBMap as K;L=K.new(A.NAMESPACE);B=await L.get(C)\n\t\telse:raise RuntimeError(A.ERROR)\n\telse:\n\t\tD=await A.idb.has(C)\n\t\tif D:B=await A.idb.get(C)\n\t\telse:B=await A.getFileSystemDirectoryHandle(H(F));await A.idb.set(C,B)\n\tmounted[E]=await I.mountNativeFS(E,B)\nasync def sync(path):await mounted[path].syncfs()\nasync def unmount(path):from _pyscript import interpreter as A;await sync(path);A._module.FS.unmount(path)",
|
||||||
|
"magic_js.py": "import json,sys,js as globalThis\nfrom polyscript import config as _config,js_modules\nfrom pyscript.util import NotSupported\nRUNNING_IN_WORKER=not hasattr(globalThis,'document')\nconfig=json.loads(globalThis.JSON.stringify(_config))\nif'MicroPython'in sys.version:config['type']='mpy'\nelse:config['type']='py'\nclass JSModule:\n\tdef __init__(A,name):A.name=name\n\tdef __getattr__(B,field):\n\t\tA=field\n\t\tif not A.startswith('_'):return getattr(getattr(js_modules,B.name),A)\nfor name in globalThis.Reflect.ownKeys(js_modules):sys.modules[f\"pyscript.js_modules.{name}\"]=JSModule(name)\nsys.modules['pyscript.js_modules']=js_modules\nif RUNNING_IN_WORKER:\n\timport polyscript;PyWorker=NotSupported('pyscript.PyWorker','pyscript.PyWorker works only when running in the main thread')\n\ttry:import js;window=polyscript.xworker.window;document=window.document;js.document=document;js_import=window.Function('return (...urls) => Promise.all(urls.map((url) => import(url)))')()\n\texcept:message='Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer';globalThis.console.warn(message);window=NotSupported('pyscript.window',message);document=NotSupported('pyscript.document',message);js_import=None\n\tsync=polyscript.xworker.sync\n\tdef current_target():return polyscript.target\nelse:\n\timport _pyscript;from _pyscript import PyWorker,js_import;window=globalThis;document=globalThis.document;sync=NotSupported('pyscript.sync','pyscript.sync works only when running in a worker')\n\tdef current_target():return _pyscript.target",
|
||||||
"media.py": "from pyscript import window\nfrom pyscript.ffi import to_js\nclass Device:\n\tdef __init__(A,device):A._dom_element=device\n\t@property\n\tdef id(self):return self._dom_element.deviceId\n\t@property\n\tdef group(self):return self._dom_element.groupId\n\t@property\n\tdef kind(self):return self._dom_element.kind\n\t@property\n\tdef label(self):return self._dom_element.label\n\tdef __getitem__(A,key):return getattr(A,key)\n\t@classmethod\n\tasync def load(E,audio=False,video=True):\n\t\tB=video;A=window.Object.new();A.audio=audio\n\t\tif isinstance(B,bool):A.video=B\n\t\telse:\n\t\t\tA.video=window.Object.new()\n\t\t\tfor C in B:setattr(A.video,C,to_js(B[C]))\n\t\tD=await window.navigator.mediaDevices.getUserMedia(A);return D\n\tasync def get_stream(A):B=A.kind.replace('input','').replace('output','');C={B:{'deviceId':{'exact':A.id}}};return await A.load(**C)\nasync def list_devices():return[Device(A)for A in await window.navigator.mediaDevices.enumerateDevices()]",
|
"media.py": "from pyscript import window\nfrom pyscript.ffi import to_js\nclass Device:\n\tdef __init__(A,device):A._dom_element=device\n\t@property\n\tdef id(self):return self._dom_element.deviceId\n\t@property\n\tdef group(self):return self._dom_element.groupId\n\t@property\n\tdef kind(self):return self._dom_element.kind\n\t@property\n\tdef label(self):return self._dom_element.label\n\tdef __getitem__(A,key):return getattr(A,key)\n\t@classmethod\n\tasync def load(E,audio=False,video=True):\n\t\tB=video;A=window.Object.new();A.audio=audio\n\t\tif isinstance(B,bool):A.video=B\n\t\telse:\n\t\t\tA.video=window.Object.new()\n\t\t\tfor C in B:setattr(A.video,C,to_js(B[C]))\n\t\tD=await window.navigator.mediaDevices.getUserMedia(A);return D\n\tasync def get_stream(A):B=A.kind.replace('input','').replace('output','');C={B:{'deviceId':{'exact':A.id}}};return await A.load(**C)\nasync def list_devices():return[Device(A)for A in await window.navigator.mediaDevices.enumerateDevices()]",
|
||||||
"storage.py": "_C='memoryview'\n_B='bytearray'\n_A='generic'\nfrom polyscript import storage as _storage\nfrom pyscript.flatted import parse as _parse\nfrom pyscript.flatted import stringify as _stringify\ndef _to_idb(value):\n\tA=value\n\tif A is None:return _stringify(['null',0])\n\tif isinstance(A,(bool,float,int,str,list,dict,tuple)):return _stringify([_A,A])\n\tif isinstance(A,bytearray):return _stringify([_B,[A for A in A]])\n\tif isinstance(A,memoryview):return _stringify([_C,[A for A in A]])\n\traise TypeError(f\"Unexpected value: {A}\")\ndef _from_idb(value):\n\tC=value;A,B=_parse(C)\n\tif A=='null':return\n\tif A==_A:return B\n\tif A==_B:return bytearray(B)\n\tif A==_C:return memoryview(bytearray(B))\n\treturn C\nclass Storage(dict):\n\tdef __init__(B,store):A=store;super().__init__({A:_from_idb(B)for(A,B)in A.entries()});B.__store__=A\n\tdef __delitem__(A,attr):A.__store__.delete(attr);super().__delitem__(attr)\n\tdef __setitem__(B,attr,value):A=value;B.__store__.set(attr,_to_idb(A));super().__setitem__(attr,A)\n\tdef clear(A):A.__store__.clear();super().clear()\n\tasync def sync(A):await A.__store__.sync()\nasync def storage(name='',storage_class=Storage):\n\tif not name:raise ValueError('The storage name must be defined')\n\treturn storage_class(await _storage(f\"@pyscript/{name}\"))",
|
"storage.py": "_C='memoryview'\n_B='bytearray'\n_A='generic'\nfrom polyscript import storage as _storage\nfrom pyscript.flatted import parse as _parse\nfrom pyscript.flatted import stringify as _stringify\ndef _to_idb(value):\n\tA=value\n\tif A is None:return _stringify(['null',0])\n\tif isinstance(A,(bool,float,int,str,list,dict,tuple)):return _stringify([_A,A])\n\tif isinstance(A,bytearray):return _stringify([_B,[A for A in A]])\n\tif isinstance(A,memoryview):return _stringify([_C,[A for A in A]])\n\traise TypeError(f\"Unexpected value: {A}\")\ndef _from_idb(value):\n\tC=value;A,B=_parse(C)\n\tif A=='null':return\n\tif A==_A:return B\n\tif A==_B:return bytearray(B)\n\tif A==_C:return memoryview(bytearray(B))\n\treturn C\nclass Storage(dict):\n\tdef __init__(B,store):A=store;super().__init__({A:_from_idb(B)for(A,B)in A.entries()});B.__store__=A\n\tdef __delitem__(A,attr):A.__store__.delete(attr);super().__delitem__(attr)\n\tdef __setitem__(B,attr,value):A=value;B.__store__.set(attr,_to_idb(A));super().__setitem__(attr,A)\n\tdef clear(A):A.__store__.clear();super().clear()\n\tasync def sync(A):await A.__store__.sync()\nasync def storage(name='',storage_class=Storage):\n\tif not name:raise ValueError('The storage name must be defined')\n\treturn storage_class(await _storage(f\"@pyscript/{name}\"))",
|
||||||
"util.py": "import js,sys,inspect\ndef as_bytearray(buffer):\n\tA=js.Uint8Array.new(buffer);B=A.length;C=bytearray(B)\n\tfor D in range(0,B):C[D]=A[D]\n\treturn C\nclass NotSupported:\n\tdef __init__(A,name,error):object.__setattr__(A,'name',name);object.__setattr__(A,'error',error)\n\tdef __repr__(A):return f\"<NotSupported {A.name} [{A.error}]>\"\n\tdef __getattr__(A,attr):raise AttributeError(A.error)\n\tdef __setattr__(A,attr,value):raise AttributeError(A.error)\n\tdef __call__(A,*B):raise TypeError(A.error)\ndef is_awaitable(obj):\n\tA=obj;from pyscript import config as B\n\tif B['type']=='mpy':\n\t\tif'<closure <generator>'in repr(A):return True\n\t\treturn inspect.isgeneratorfunction(A)\n\treturn inspect.iscoroutinefunction(A)",
|
"util.py": "import js,sys,inspect\ndef as_bytearray(buffer):\n\tA=js.Uint8Array.new(buffer);B=A.length;C=bytearray(B)\n\tfor D in range(0,B):C[D]=A[D]\n\treturn C\nclass NotSupported:\n\tdef __init__(A,name,error):object.__setattr__(A,'name',name);object.__setattr__(A,'error',error)\n\tdef __repr__(A):return f\"<NotSupported {A.name} [{A.error}]>\"\n\tdef __getattr__(A,attr):raise AttributeError(A.error)\n\tdef __setattr__(A,attr,value):raise AttributeError(A.error)\n\tdef __call__(A,*B):raise TypeError(A.error)\ndef is_awaitable(obj):\n\tA=obj;from pyscript import config as B\n\tif B['type']=='mpy':\n\t\tif'<closure <generator>'in repr(A):return True\n\t\treturn inspect.isgeneratorfunction(A)\n\treturn inspect.iscoroutinefunction(A)",
|
||||||
|
|||||||
60
core/src/stdlib/pyscript/fs.py
Normal file
60
core/src/stdlib/pyscript/fs.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
mounted = {}
|
||||||
|
|
||||||
|
|
||||||
|
async def mount(path, mode="readwrite", root="", id="pyscript"):
|
||||||
|
import js
|
||||||
|
from _pyscript import fs, interpreter
|
||||||
|
from pyscript.ffi import to_js
|
||||||
|
from pyscript.magic_js import (
|
||||||
|
RUNNING_IN_WORKER,
|
||||||
|
sync,
|
||||||
|
)
|
||||||
|
|
||||||
|
js.console.warn("experimental pyscript.fs ⚠️")
|
||||||
|
|
||||||
|
handler = None
|
||||||
|
|
||||||
|
uid = f"{path}@{id}"
|
||||||
|
|
||||||
|
options = {"id": id, "mode": mode}
|
||||||
|
if root != "":
|
||||||
|
options["startIn"] = root
|
||||||
|
|
||||||
|
if RUNNING_IN_WORKER:
|
||||||
|
fsh = sync.storeFSHandler(uid, to_js(options))
|
||||||
|
|
||||||
|
# allow both async and/or SharedArrayBuffer use case
|
||||||
|
if isinstance(fsh, bool):
|
||||||
|
success = fsh
|
||||||
|
else:
|
||||||
|
success = await fsh
|
||||||
|
|
||||||
|
if success:
|
||||||
|
from polyscript import IDBMap
|
||||||
|
|
||||||
|
idb = IDBMap.new(fs.NAMESPACE)
|
||||||
|
handler = await idb.get(uid)
|
||||||
|
else:
|
||||||
|
raise RuntimeError(fs.ERROR)
|
||||||
|
|
||||||
|
else:
|
||||||
|
success = await fs.idb.has(uid)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
handler = await fs.idb.get(uid)
|
||||||
|
else:
|
||||||
|
handler = await fs.getFileSystemDirectoryHandle(to_js(options))
|
||||||
|
await fs.idb.set(uid, handler)
|
||||||
|
|
||||||
|
mounted[path] = await interpreter.mountNativeFS(path, handler)
|
||||||
|
|
||||||
|
|
||||||
|
async def sync(path):
|
||||||
|
await mounted[path].syncfs()
|
||||||
|
|
||||||
|
|
||||||
|
async def unmount(path):
|
||||||
|
from _pyscript import interpreter
|
||||||
|
|
||||||
|
await sync(path)
|
||||||
|
interpreter._module.FS.unmount(path)
|
||||||
@@ -45,8 +45,6 @@ if RUNNING_IN_WORKER:
|
|||||||
|
|
||||||
window = polyscript.xworker.window
|
window = polyscript.xworker.window
|
||||||
document = window.document
|
document = window.document
|
||||||
# weird + not worth it as it does not work anyway
|
|
||||||
js.screen = window.screen
|
|
||||||
js.document = document
|
js.document = document
|
||||||
# this is the same as js_import on main and it lands modules on main
|
# this is the same as js_import on main and it lands modules on main
|
||||||
js_import = window.Function(
|
js_import = window.Function(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { idb, getFileSystemDirectoryHandle } from "./fs.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// allow pyterminal checks to bootstrap
|
// allow pyterminal checks to bootstrap
|
||||||
is_pyterminal: () => false,
|
is_pyterminal: () => false,
|
||||||
@@ -9,4 +11,21 @@ export default {
|
|||||||
sleep(seconds) {
|
sleep(seconds) {
|
||||||
return new Promise(($) => setTimeout($, seconds * 1000));
|
return new Promise(($) => setTimeout($, seconds * 1000));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask a user action via dialog and returns the directory handler once granted.
|
||||||
|
* @param {string} uid
|
||||||
|
* @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async storeFSHandler(uid, options = {}) {
|
||||||
|
if (await idb.has(uid)) return true;
|
||||||
|
return getFileSystemDirectoryHandle(options).then(
|
||||||
|
async (handler) => {
|
||||||
|
await idb.set(uid, handler);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
() => false,
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
12
core/tests/manual/fs/index.html
Normal file
12
core/tests/manual/fs/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="../../../dist/core.css">
|
||||||
|
<script type="module" src="../../../dist/core.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="mpy" src="index.py"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
46
core/tests/manual/fs/index.py
Normal file
46
core/tests/manual/fs/index.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import os
|
||||||
|
from pyscript import RUNNING_IN_WORKER, fs
|
||||||
|
|
||||||
|
|
||||||
|
TEST = "implicit"
|
||||||
|
|
||||||
|
if TEST == "implicit":
|
||||||
|
await fs.mount("/persistent")
|
||||||
|
|
||||||
|
print(
|
||||||
|
RUNNING_IN_WORKER and "Worker" or "Main",
|
||||||
|
os.listdir("/persistent"),
|
||||||
|
)
|
||||||
|
|
||||||
|
from random import random
|
||||||
|
|
||||||
|
with open("/persistent/random.txt", "w") as f:
|
||||||
|
f.write(str(random()))
|
||||||
|
|
||||||
|
await fs.sync("/persistent")
|
||||||
|
|
||||||
|
elif not RUNNING_IN_WORKER:
|
||||||
|
from pyscript import document
|
||||||
|
|
||||||
|
button = document.createElement("button")
|
||||||
|
button.textContent = "mount"
|
||||||
|
document.body.append(button)
|
||||||
|
|
||||||
|
async def mount(event):
|
||||||
|
try:
|
||||||
|
await fs.mount("/persistent")
|
||||||
|
print(os.listdir("/persistent"))
|
||||||
|
button.textContent = "unmount"
|
||||||
|
button.onclick = unmount
|
||||||
|
|
||||||
|
except:
|
||||||
|
import js
|
||||||
|
|
||||||
|
js.alert("unable to grant access")
|
||||||
|
|
||||||
|
async def unmount(event):
|
||||||
|
await fs.unmount("/persistent")
|
||||||
|
button.textContent = "mount"
|
||||||
|
button.onclick = mount
|
||||||
|
|
||||||
|
button.onclick = mount
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
""" (c) https://github.com/ryanking13/pyodide-pygame-demo/blob/main/examples/aliens.html
|
"""(c) https://github.com/ryanking13/pyodide-pygame-demo/blob/main/examples/aliens.html
|
||||||
pygame.examples.aliens
|
pygame.examples.aliens
|
||||||
|
|
||||||
Shows a mini game where you have to defend against aliens.
|
Shows a mini game where you have to defend against aliens.
|
||||||
@@ -22,11 +22,14 @@ Controls
|
|||||||
* f key to toggle between fullscreen.
|
* f key to toggle between fullscreen.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
import pyscript
|
||||||
|
|
||||||
# import basic pygame modules
|
# import basic pygame modules
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ SCORE = 0
|
|||||||
|
|
||||||
main_dir = str(pathlib.Path(pygame.__file__).parent / "examples")
|
main_dir = str(pathlib.Path(pygame.__file__).parent / "examples")
|
||||||
|
|
||||||
|
|
||||||
def load_image(file):
|
def load_image(file):
|
||||||
"""loads an image, prepares it for play"""
|
"""loads an image, prepares it for play"""
|
||||||
file = os.path.join(main_dir, "data", file)
|
file = os.path.join(main_dir, "data", file)
|
||||||
@@ -391,4 +395,5 @@ async def main(winstyle=0):
|
|||||||
if pygame.mixer:
|
if pygame.mixer:
|
||||||
pygame.mixer.music.fadeout(1000)
|
pygame.mixer.music.fadeout(1000)
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -8,12 +8,7 @@
|
|||||||
<script type="module" src="../../../dist/core.js"></script>
|
<script type="module" src="../../../dist/core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script
|
<script type="py-game" src="aliens.py"></script>
|
||||||
type="py"
|
|
||||||
src="aliens.py"
|
|
||||||
canvas="canvas"
|
|
||||||
config='{"packages":["pygame-ce"]}'
|
|
||||||
></script>
|
|
||||||
<div class="demo">
|
<div class="demo">
|
||||||
<div class="demo-header">pygame.examples.aliens</div>
|
<div class="demo-header">pygame.examples.aliens</div>
|
||||||
<div class="demo-content">
|
<div class="demo-content">
|
||||||
|
|||||||
5
core/types/config.d.ts
vendored
5
core/types/config.d.ts
vendored
@@ -1,2 +1,7 @@
|
|||||||
|
export function configDetails(config: string, type: string | null): {
|
||||||
|
json: boolean;
|
||||||
|
toml: boolean;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
export const configs: Map<any, any>;
|
export const configs: Map<any, any>;
|
||||||
export function relative_url(url: any, base?: string): string;
|
export function relative_url(url: any, base?: string): string;
|
||||||
|
|||||||
8
core/types/fs.d.ts
vendored
Normal file
8
core/types/fs.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export const NAMESPACE: "@pyscript.fs";
|
||||||
|
export const ERROR: "storage permissions not granted";
|
||||||
|
export const idb: any;
|
||||||
|
export function getFileSystemDirectoryHandle(options: {
|
||||||
|
id?: string;
|
||||||
|
mode?: "read" | "readwrite";
|
||||||
|
hint?: "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos";
|
||||||
|
}): Promise<FileSystemDirectoryHandle>;
|
||||||
1
core/types/plugins.d.ts
vendored
1
core/types/plugins.d.ts
vendored
@@ -4,6 +4,7 @@ declare const _default: {
|
|||||||
donkey: () => Promise<typeof import("./plugins/donkey.js")>;
|
donkey: () => Promise<typeof import("./plugins/donkey.js")>;
|
||||||
error: () => Promise<typeof import("./plugins/error.js")>;
|
error: () => Promise<typeof import("./plugins/error.js")>;
|
||||||
"py-editor": () => Promise<typeof import("./plugins/py-editor.js")>;
|
"py-editor": () => Promise<typeof import("./plugins/py-editor.js")>;
|
||||||
|
"py-game": () => Promise<typeof import("./plugins/py-game.js")>;
|
||||||
"py-terminal": () => Promise<typeof import("./plugins/py-terminal.js")>;
|
"py-terminal": () => Promise<typeof import("./plugins/py-terminal.js")>;
|
||||||
};
|
};
|
||||||
export default _default;
|
export default _default;
|
||||||
|
|||||||
1
core/types/plugins/py-game.d.ts
vendored
Normal file
1
core/types/plugins/py-game.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
1
core/types/stdlib/pyscript.d.ts
vendored
1
core/types/stdlib/pyscript.d.ts
vendored
@@ -6,6 +6,7 @@ declare namespace _default {
|
|||||||
"fetch.py": string;
|
"fetch.py": string;
|
||||||
"ffi.py": string;
|
"ffi.py": string;
|
||||||
"flatted.py": string;
|
"flatted.py": string;
|
||||||
|
"fs.py": string;
|
||||||
"magic_js.py": string;
|
"magic_js.py": string;
|
||||||
"media.py": string;
|
"media.py": string;
|
||||||
"storage.py": string;
|
"storage.py": string;
|
||||||
|
|||||||
11
core/types/sync.d.ts
vendored
11
core/types/sync.d.ts
vendored
@@ -5,5 +5,16 @@ declare namespace _default {
|
|||||||
* @param {number} seconds The number of seconds to sleep.
|
* @param {number} seconds The number of seconds to sleep.
|
||||||
*/
|
*/
|
||||||
function sleep(seconds: number): Promise<any>;
|
function sleep(seconds: number): Promise<any>;
|
||||||
|
/**
|
||||||
|
* Ask a user action via dialog and returns the directory handler once granted.
|
||||||
|
* @param {string} uid
|
||||||
|
* @param {{id?:string, mode?:"read"|"readwrite", hint?:"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos"}} options
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function storeFSHandler(uid: string, options?: {
|
||||||
|
id?: string;
|
||||||
|
mode?: "read" | "readwrite";
|
||||||
|
hint?: "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos";
|
||||||
|
}): boolean;
|
||||||
}
|
}
|
||||||
export default _default;
|
export default _default;
|
||||||
|
|||||||
Reference in New Issue
Block a user