Worker sync utility (#1511)

* patched an issue with wasmoon randomly asking to resolve proxy references
  * simplified pyodide and micropython dance by grouping their common utilities together
  * created an integration test around a worker to main thread input between MicroPython and Lua
  * commented some weird bugs / funny behaviors around both MicroPython and Pyodide
  * other minor clean ups
This commit is contained in:
Andrea Giammarchi
2023-06-08 11:10:47 +02:00
committed by GitHub
parent 0cdbfbeb30
commit af72e232c3
26 changed files with 277 additions and 81 deletions

View File

@@ -14,7 +14,7 @@ repos:
- id: check-docstring-first - id: check-docstring-first
- id: check-executables-have-shebangs - id: check-executables-have-shebangs
- id: check-json - id: check-json
exclude: tsconfig.json exclude: tsconfig.json pyscript.core/cjs/package.json
- id: check-toml - id: check-toml
- id: check-xml - id: check-xml
- id: check-yaml - id: check-yaml

View File

@@ -7,4 +7,5 @@ pyscript.core/micropython/
pyscript.core/pyscript/ pyscript.core/pyscript/
pyscript.core/types/ pyscript.core/types/
pyscript.core/esm/worker/xworker.js pyscript.core/esm/worker/xworker.js
pyscript.core/cjs/package.json
pyscript.core/min.js pyscript.core/min.js

View File

@@ -0,0 +1 @@
{"type":"commonjs"}

View File

@@ -13,10 +13,11 @@ export const getRuntime = (id, config) => {
if (config) { if (config) {
// REQUIRES INTEGRATION TEST // REQUIRES INTEGRATION TEST
/* c8 ignore start */ /* c8 ignore start */
if (config.endsWith(".json")) options = fetch(config).then(getJSON); if (config.endsWith(".json")) {
else if (config.endsWith(".toml")) options = fetch(config).then(getJSON);
} else if (config.endsWith(".toml")) {
options = fetch(config).then(getText).then(parse); options = fetch(config).then(getText).then(parse);
else { } else {
try { try {
options = JSON.parse(config); options = JSON.parse(config);
} catch (_) { } catch (_) {

View File

@@ -0,0 +1,27 @@
import { clean, writeFile as writeFileUtil } from "./_utils.js";
// REQUIRES INTEGRATION TEST
/* c8 ignore start */
export const run = (runtime, code) => runtime.runPython(clean(code));
export const runAsync = (runtime, code) => runtime.runPythonAsync(clean(code));
export function runEvent(runtime, code, key) {
code = `import js;event=js.__events.get(${key});${code}`;
return this.run(runtime, code);
}
const worker = (method) =>
function (runtime, code, xworker) {
code = `from js import xworker;${code}`;
globalThis.xworker = xworker;
return this[method](runtime, code);
};
export const runWorker = worker("run");
export const runWorkerAsync = worker("runAsync");
export const writeFile = ({ FS }, path, buffer) =>
writeFileUtil(FS, path, buffer);
/* c8 ignore stop */

View File

@@ -1,10 +1,26 @@
import { getBuffer } from "../fetch-utils.js"; import { getBuffer } from "../fetch-utils.js";
import { absoluteURL } from "../utils.js"; import { absoluteURL, defineProperty } from "../utils.js";
import "@ungap/with-resolvers";
// REQUIRES INTEGRATION TEST
/* c8 ignore start */
// TODO: this should *NOT* be needed as the polyfill
// already patches on demand the Promise object
const { withResolvers } = Promise;
defineProperty(globalThis, "Promise", {
configurable: true,
value: class extends Promise {
withResolvers() {
return withResolvers.call(this);
}
},
});
/* c8 ignore stop */
/** /**
* Trim code only if it's a single line that prettier or other tools might have modified. * Trim code only if it's a single line that prettier or other tools might have modified.
* @param {string} code code that might be a single line * @param {string} code code that might be a single line
* @returns {strong} * @returns {string}
*/ */
export const clean = (code) => export const clean = (code) =>
code.replace(/^[^\r\n]+$/, (line) => line.trim()); code.replace(/^[^\r\n]+$/, (line) => line.trim());

View File

@@ -1,15 +1,17 @@
import { clean, fetchPaths, stdio, writeFile } from "./_utils.js"; import { fetchPaths, stdio } from "./_utils.js";
import {
run,
runAsync,
runEvent,
runWorker,
runWorkerAsync,
writeFile,
} from "./_python.js";
const type = "micropython"; const type = "micropython";
// REQUIRES INTEGRATION TEST // REQUIRES INTEGRATION TEST
/* c8 ignore start */ /* c8 ignore start */
const worker = (method) =>
function (runtime, code, xworker) {
globalThis.xworker = xworker;
return this[method](runtime, `from js import xworker;${code}`);
};
export default { export default {
type: [type, "mpy"], type: [type, "mpy"],
module: () => `http://localhost:8080/micropython/micropython.mjs`, module: () => `http://localhost:8080/micropython/micropython.mjs`,
@@ -20,16 +22,11 @@ export default {
if (config.fetch) await fetchPaths(this, runtime, config.fetch); if (config.fetch) await fetchPaths(this, runtime, config.fetch);
return runtime; return runtime;
}, },
run: (runtime, code) => runtime.runPython(clean(code)), run,
runAsync: (runtime, code) => runtime.runPythonAsync(clean(code)), runAsync,
runEvent(runtime, code, key) { runEvent,
return this.run( runWorker,
runtime, runWorkerAsync,
`import js;event=js.__events.get(${key});${code}`, writeFile,
);
},
runWorker: worker("run"),
runWorkerAsync: worker("runAsync"),
writeFile: ({ FS }, path, buffer) => writeFile(FS, path, buffer),
}; };
/* c8 ignore stop */ /* c8 ignore stop */

View File

@@ -1,15 +1,17 @@
import { clean, fetchPaths, stdio, writeFile } from "./_utils.js"; import { fetchPaths, stdio } from "./_utils.js";
import {
run,
runAsync,
runEvent,
runWorker,
runWorkerAsync,
writeFile,
} from "./_python.js";
const type = "pyodide"; const type = "pyodide";
// REQUIRES INTEGRATION TEST // REQUIRES INTEGRATION TEST
/* c8 ignore start */ /* c8 ignore start */
const worker = (method) =>
function (runtime, code, xworker) {
globalThis.xworker = xworker;
return this[method](runtime, `from js import xworker;${code}`);
};
export default { export default {
type: [type, "py"], type: [type, "py"],
module: (version = "0.22.1") => module: (version = "0.22.1") =>
@@ -26,16 +28,11 @@ export default {
} }
return runtime; return runtime;
}, },
run: (runtime, code) => runtime.runPython(clean(code)), run,
runAsync: (runtime, code) => runtime.runPythonAsync(clean(code)), runAsync,
runEvent(runtime, code, key) { runEvent,
return this.run( runWorker,
runtime, runWorkerAsync,
`import js;event=js.__events.get(${key});${code}`, writeFile,
);
},
runWorker: worker("run"),
runWorkerAsync: worker("runAsync"),
writeFile: ({ FS }, path, buffer) => writeFile(FS, path, buffer),
}; };
/* c8 ignore stop */ /* c8 ignore stop */

View File

@@ -1,5 +1,5 @@
// lazy TOML parser (fast-toml might be a better alternative) // lazy TOML parser (fast-toml might be a better alternative)
const TOML_LIB = `https://unpkg.com/basic-toml@0.3.1/es.js`; const TOML_LIB = `https://cdn.jsdelivr.net/npm/basic-toml@0.3.1/es.js`;
/** /**
* @param {string} text TOML text to parse * @param {string} text TOML text to parse

View File

@@ -1,6 +1,6 @@
const { isArray } = Array; const { isArray } = Array;
const { assign, create, defineProperty } = Object; const { assign, create, defineProperties, defineProperty } = Object;
const { all, resolve } = new Proxy(Promise, { const { all, resolve } = new Proxy(Promise, {
get: ($, name) => $[name].bind($), get: ($, name) => $[name].bind($),
@@ -8,4 +8,13 @@ const { all, resolve } = new Proxy(Promise, {
const absoluteURL = (path, base = location.href) => new URL(path, base).href; const absoluteURL = (path, base = location.href) => new URL(path, base).href;
export { isArray, assign, create, defineProperty, all, resolve, absoluteURL }; export {
isArray,
assign,
create,
defineProperties,
defineProperty,
all,
resolve,
absoluteURL,
};

View File

@@ -4,6 +4,8 @@
// Please check via `npm run size` that worker code is not much // Please check via `npm run size` that worker code is not much
// bigger than it used to be before any changes is applied to this file. // bigger than it used to be before any changes is applied to this file.
import coincident from "coincident/structured";
import { registry } from "../runtimes.js"; import { registry } from "../runtimes.js";
import { getRuntime, getRuntimeID } from "../loader.js"; import { getRuntime, getRuntimeID } from "../loader.js";
@@ -20,7 +22,11 @@ const add = (type, fn) => {
!!fn && { once: true }, !!fn && { once: true },
); );
}; };
const xworker = { const xworker = {
// allows synchronous utilities between this worker and the main thread
sync: coincident(self),
// standard worker related events / features
onerror() {}, onerror() {},
onmessage() {}, onmessage() {},
onmessageerror() {}, onmessageerror() {},
@@ -35,16 +41,14 @@ const xworker = {
return event; return event;
}, },
}; };
add("message", ({ data: { options, code } }) => { add("message", ({ data: { options, code } }) => {
engine = (async () => { engine = (async () => {
const { type, version, config, async: isAsync } = options; const { type, version, config, async: isAsync } = options;
const engine = await getRuntime(getRuntimeID(type, version), config); const engine = await getRuntime(getRuntimeID(type, version), config);
const details = registry.get(type); const details = registry.get(type);
(run = details[`runWorker${isAsync ? "Async" : ""}`].bind(details))( run = details[`runWorker${isAsync ? "Async" : ""}`].bind(details);
engine, run(engine, code, xworker);
code,
(globalThis.xworker = xworker),
);
return engine; return engine;
})(); })();
add("error"); add("error");

View File

@@ -1,5 +1,6 @@
import coincident from "coincident/structured";
import xworker from "./xworker.js"; import xworker from "./xworker.js";
import { assign, defineProperty, absoluteURL } from "../utils.js"; import { assign, defineProperties, absoluteURL } from "../utils.js";
import { getText } from "../fetch-utils.js"; import { getText } from "../fetch-utils.js";
/** /**
@@ -28,8 +29,15 @@ export default (...args) =>
const bootstrap = fetch(url) const bootstrap = fetch(url)
.then(getText) .then(getText)
.then((code) => postMessage.call(worker, { options, code })); .then((code) => postMessage.call(worker, { options, code }));
return defineProperty(worker, "postMessage", { return defineProperties(worker, {
value: (data, ...rest) => postMessage: {
bootstrap.then(() => postMessage.call(worker, data, ...rest)), value: (data, ...rest) =>
bootstrap.then(() =>
postMessage.call(worker, data, ...rest),
),
},
sync: {
value: coincident(worker),
},
}); });
}; };

View File

@@ -3,6 +3,6 @@
"http://pyodide": "./test/mocked/pyodide.mjs", "http://pyodide": "./test/mocked/pyodide.mjs",
"https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.mjs": "./test/mocked/pyodide.mjs", "https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.mjs": "./test/mocked/pyodide.mjs",
"http://localhost:8080/micropython/micropython.mjs": "./test/mocked/micropython.mjs", "http://localhost:8080/micropython/micropython.mjs": "./test/mocked/micropython.mjs",
"https://unpkg.com/basic-toml@0.3.1/es.js": "./test/mocked/toml.mjs" "https://cdn.jsdelivr.net/npm/basic-toml@0.3.1/es.js": "./test/mocked/toml.mjs"
} }
} }

View File

@@ -9,7 +9,9 @@
"version": "0.0.0", "version": "0.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"basic-devtools": "^0.1.6" "@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"coincident": "^0.2.3"
}, },
"devDependencies": { "devDependencies": {
"@node-loader/import-maps": "^1.1.0", "@node-loader/import-maps": "^1.1.0",
@@ -19,8 +21,8 @@
"c8": "^7.14.0", "c8": "^7.14.0",
"eslint": "^8.42.0", "eslint": "^8.42.0",
"linkedom": "^0.14.26", "linkedom": "^0.14.26",
"rollup": "^3.23.1", "rollup": "^3.24.0",
"static-handler": "^0.4.0", "static-handler": "^0.4.1",
"typescript": "^5.1.3" "typescript": "^5.1.3"
} }
}, },
@@ -335,6 +337,16 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true "dev": true
}, },
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
},
"node_modules/@ungap/with-resolvers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@ungap/with-resolvers/-/with-resolvers-0.1.0.tgz",
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.8.2", "version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -521,6 +533,14 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"node_modules/coincident": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.2.3.tgz",
"integrity": "sha512-zCHSEa8x6HcXTaPQLH89//mu7UQn/Iie2xcSOhTMPu546lboT7Js2vr1HIlZHISe82++0y7JSnX/7gIWy0bcrw==",
"dependencies": {
"@ungap/structured-clone": "^1.2.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1647,9 +1667,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "3.23.1", "version": "3.24.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.24.0.tgz",
"integrity": "sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==", "integrity": "sha512-OgraHOIg2YpHQTjl0/ymWfFNBEyPucB7lmhXrQUh38qNOegxLapSPFs9sNr0qKR75awW41D93XafoR2QfhBdUQ==",
"dev": true, "dev": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@@ -1776,9 +1796,9 @@
} }
}, },
"node_modules/static-handler": { "node_modules/static-handler": {
"version": "0.4.0", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.0.tgz", "resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.1.tgz",
"integrity": "sha512-ixtyrMNaN6Z0jW4RDJWL9BJT2kZ4wX05+D1XNGOOXxeUqrpk4MhZtMXdqQGxitRSrDCO6/Esjdq28rrvzDeYEw==", "integrity": "sha512-atl+UofbqCECS4Ag1ZZIjC606I1I40CuDsdq4YQ5Vq3vTsUbvqVaNG4evFsKktlnPcEm217vBUSEFKyi9lsJ8Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"mime-types": "^2.1.34" "mime-types": "^2.1.34"
@@ -2275,6 +2295,16 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true "dev": true
}, },
"@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
},
"@ungap/with-resolvers": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@ungap/with-resolvers/-/with-resolvers-0.1.0.tgz",
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
},
"acorn": { "acorn": {
"version": "8.8.2", "version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -2416,6 +2446,14 @@
"wrap-ansi": "^7.0.0" "wrap-ansi": "^7.0.0"
} }
}, },
"coincident": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.2.3.tgz",
"integrity": "sha512-zCHSEa8x6HcXTaPQLH89//mu7UQn/Iie2xcSOhTMPu546lboT7Js2vr1HIlZHISe82++0y7JSnX/7gIWy0bcrw==",
"requires": {
"@ungap/structured-clone": "^1.2.0"
}
},
"color-convert": { "color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3243,9 +3281,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "3.23.1", "version": "3.24.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.24.0.tgz",
"integrity": "sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==", "integrity": "sha512-OgraHOIg2YpHQTjl0/ymWfFNBEyPucB7lmhXrQUh38qNOegxLapSPFs9sNr0qKR75awW41D93XafoR2QfhBdUQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
@@ -3325,9 +3363,9 @@
} }
}, },
"static-handler": { "static-handler": {
"version": "0.4.0", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.0.tgz", "resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.1.tgz",
"integrity": "sha512-ixtyrMNaN6Z0jW4RDJWL9BJT2kZ4wX05+D1XNGOOXxeUqrpk4MhZtMXdqQGxitRSrDCO6/Esjdq28rrvzDeYEw==", "integrity": "sha512-atl+UofbqCECS4Ag1ZZIjC606I1I40CuDsdq4YQ5Vq3vTsUbvqVaNG4evFsKktlnPcEm217vBUSEFKyi9lsJ8Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"mime-types": "^2.1.34" "mime-types": "^2.1.34"

View File

@@ -5,7 +5,7 @@
"main": "./cjs/index.js", "main": "./cjs/index.js",
"types": "./types/index.d.ts", "types": "./types/index.d.ts",
"scripts": { "scripts": {
"server": "npx static-handler --cors --coep --coop .", "server": "npx static-handler --cors --coep --coop --corp .",
"build": "npm run rollup:xworker && npm run rollup:min && eslint esm/ && npm run ts && npm run cjs && npm run test", "build": "npm run rollup:xworker && npm run rollup:min && eslint esm/ && npm run ts && npm run cjs && npm run test",
"cjs": "ascjs --no-default esm cjs", "cjs": "ascjs --no-default esm cjs",
"rollup:min": "rollup --config rollup/min.config.js", "rollup:min": "rollup --config rollup/min.config.js",
@@ -29,8 +29,8 @@
"c8": "^7.14.0", "c8": "^7.14.0",
"eslint": "^8.42.0", "eslint": "^8.42.0",
"linkedom": "^0.14.26", "linkedom": "^0.14.26",
"rollup": "^3.23.1", "rollup": "^3.24.0",
"static-handler": "^0.4.0", "static-handler": "^0.4.1",
"typescript": "^5.1.3" "typescript": "^5.1.3"
}, },
"module": "./esm/index.js", "module": "./esm/index.js",
@@ -45,9 +45,11 @@
}, },
"unpkg": "min.js", "unpkg": "min.js",
"dependencies": { "dependencies": {
"basic-devtools": "^0.1.6" "@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"coincident": "^0.2.3"
}, },
"worker": { "worker": {
"blob": "sha256-kYwW0+fdul6LFbjoFeqku+pa9ARFxRtfmeM6zF53zLo=" "blob": "sha256-T0DDfoQde+3gciIoHqPGP2SgMQpbY/nAMjfd1cg8jyA="
} }
} }

View File

@@ -10,7 +10,7 @@ createRequire(import.meta.url)("./build_xworker.cjs");
export default { export default {
input: "./esm/index.js", input: "./esm/index.js",
plugins: [nodeResolve(), terser()], plugins: process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
output: { output: {
esModule: true, esModule: true,
file: "./min.js", file: "./min.js",

View File

@@ -16,7 +16,7 @@ const WORKERS_DIR = resolve(
export default { export default {
input: join(WORKERS_DIR, "_template.js"), input: join(WORKERS_DIR, "_template.js"),
plugins: [nodeResolve(), terser()], plugins: process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
output: { output: {
esModule: true, esModule: true,
file: join(WORKERS_DIR, "__template.js"), file: join(WORKERS_DIR, "__template.js"),

View File

@@ -8,7 +8,9 @@
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
"basic-devtools": "../node_modules/basic-devtools/esm/index.js" "basic-devtools": "../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
} }
} }
</script> </script>

View File

@@ -8,7 +8,9 @@
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
"basic-devtools": "../node_modules/basic-devtools/esm/index.js" "basic-devtools": "../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
} }
} }
</script> </script>

View File

@@ -8,7 +8,9 @@
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
"basic-devtools": "../node_modules/basic-devtools/esm/index.js" "basic-devtools": "../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
} }
} }
</script> </script>

View File

@@ -8,7 +8,9 @@
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
"basic-devtools": "../node_modules/basic-devtools/esm/index.js" "basic-devtools": "../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
} }
} }
</script> </script>

View File

@@ -8,6 +8,8 @@
{ {
"imports": { "imports": {
"basic-devtools": "../../node_modules/basic-devtools/esm/index.js", "basic-devtools": "../../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
"@pyscript/core": "../../esm/index.js" "@pyscript/core": "../../esm/index.js"
} }
} }

View File

@@ -0,0 +1,73 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>python workers</title>
<script type="importmap">
{
"imports": {
"basic-devtools": "../../node_modules/basic-devtools/esm/index.js",
"coincident/structured": "../../node_modules/coincident/structured.js",
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
"@pyscript/core": "../../esm/index.js"
}
}
</script>
<script type="module">
import "@pyscript/core";
</script>
</head>
<body>
<script type="mpy">
from js import XWorker, Promise, document
deferred = Promise.withResolvers()
def handle_result(event):
inputs = document.querySelectorAll('input')
inputs[0].disabled = True
inputs[1].disabled = True
deferred.resolve(inputs[0].value)
def handle_input(data):
inputs = document.querySelectorAll('input')
inputs[0].placeholder = data
inputs[0].disabled = False
inputs[1].disabled = False
return deferred.promise
w = XWorker('./input.lua', type='lua')
w.sync.input = handle_input
</script>
<input type="text" placeholder="loading ..." required disabled />
<input type="submit" mpy-click="handle_result(event)" disabled />
<!--script type="mpy">
from js import XWorker, Array, Promise, Reflect, document
resolve = None
def trap_resolve(res, rej):
global resolve
resolve = res
def handle_result(event):
input = document.querySelector('input[type="text"]')
resolve(input.value)
def handle_input(data):
input = document.querySelector('input[type="text"]')
input.placeholder = data
input.disabled = False
# TODO: Promise.new(trap_resolve) does not work in MicroPython
# but the following throws in Pyodide 😅 so fallback to deferred
return Reflect.construct(Promise, Array(trap_resolve))
# TODO: Proxy do not work in MicroPython so type='py' it is
w = XWorker('./input.py', type='py')
w.sync.input = handle_input
</script-->
</body>
</html>

View File

@@ -0,0 +1,2 @@
print('What is 2 + 3?')
print('Answer: ' .. xworker.sync.input('What is 2 + 3?'))

View File

@@ -0,0 +1,4 @@
from js import xworker
print("What is 2 + 3?")
print("Answer: " + xworker.sync.input("What is 2 + 3?"))

View File

@@ -0,0 +1,6 @@
require "js"
xworker = JS::eval("return xworker")
puts "What is 2 + 3?"
puts xworker.sync.input("What is 2 + 3?")