mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-19 18:27:29 -05:00
Implement PyScript custom <script type> (#1548)
* updated MicroPython to latest in order to have `globals` API available * reduced code around helpers for both MicroPython and Pyodide as now these are more aligned * updated all dependencies and brought in latest [coincident/window](https://github.com/WebReflection/coincident#coincidentwindow) goodness to any `xworker`, preserving the `sync` previous behavior * using [@ungap/structured-clone/json](https://github.com/ungap/structured-clone#tojson) as *coincident* default `parse` and `stringify` utility to allow recursive and more complex data to travel back from the *Worker* (forward data is still fully [structured clone algorithm compatible](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)) * renamed all *plugin/s* references to *custom/s* as plugin as a word was too misleading * changed *custom types* helpers logic to allow any single node to have its own version of the interpreter wrapper, and all the extra fields it carries with it, including a way to augment every interpreter execution, among as every worker code execution * created a `custom` folder where I've landed the very first `pyscript.js` custom type * created an exhaustive test page to demonstrate the current abilities of *PyScript Next* among its ability to expose utilities that can be used to create *PyScript* plugins
This commit is contained in:
committed by
GitHub
parent
0a7e1ce0d7
commit
f6dfc5361e
@@ -9,3 +9,4 @@ pyscript.core/types/
|
|||||||
pyscript.core/esm/worker/xworker.js
|
pyscript.core/esm/worker/xworker.js
|
||||||
pyscript.core/cjs/package.json
|
pyscript.core/cjs/package.json
|
||||||
pyscript.core/min.js
|
pyscript.core/min.js
|
||||||
|
pyscript.core/pyscript.js
|
||||||
|
|||||||
2
pyscript.core/.gitignore
vendored
2
pyscript.core/.gitignore
vendored
@@ -3,6 +3,6 @@ coverage/
|
|||||||
node_modules/
|
node_modules/
|
||||||
cjs/
|
cjs/
|
||||||
!cjs/package.json
|
!cjs/package.json
|
||||||
min.js
|
core.js
|
||||||
esm/worker/xworker.js
|
esm/worker/xworker.js
|
||||||
types/
|
types/
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ This project requires some automatic artifact creation to:
|
|||||||
|
|
||||||
* create a _Worker_ as a _Blob_ based on the same code used by this repo
|
* create a _Worker_ as a _Blob_ based on the same code used by this repo
|
||||||
* create automatically the list of runtimes available via the module
|
* create automatically the list of runtimes available via the module
|
||||||
* create the `min.js` file used by most integration tests
|
* create the `core.js` file used by most integration tests
|
||||||
* create a sha256 version of the Blob content for CSP cases
|
* create a sha256 version of the Blob content for CSP cases
|
||||||
|
|
||||||
Accordingly, to build latest project:
|
Accordingly, to build latest project:
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import { getRuntimeID } from "./loader.js";
|
|||||||
import { io } from "./interpreter/_utils.js";
|
import { io } from "./interpreter/_utils.js";
|
||||||
import { addAllListeners } from "./listeners.js";
|
import { addAllListeners } from "./listeners.js";
|
||||||
|
|
||||||
import workerHooks from "./worker/hooks.js";
|
|
||||||
|
|
||||||
export const CUSTOM_SELECTORS = [];
|
export const CUSTOM_SELECTORS = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +24,6 @@ export const CUSTOM_SELECTORS = [];
|
|||||||
* @prop {(path:string, data:ArrayBuffer) => void} writeFile an utility to write a file in the virtual FS, if available
|
* @prop {(path:string, data:ArrayBuffer) => void} writeFile an utility to write a file in the virtual FS, if available
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const patched = new Map();
|
|
||||||
const types = new Map();
|
const types = new Map();
|
||||||
const waitList = new Map();
|
const waitList = new Map();
|
||||||
|
|
||||||
@@ -52,7 +49,7 @@ export const handleCustomType = (node) => {
|
|||||||
} = options;
|
} = options;
|
||||||
const name = getRuntimeID(runtime, version);
|
const name = getRuntimeID(runtime, version);
|
||||||
const id = env || `${name}${config ? `|${config}` : ""}`;
|
const id = env || `${name}${config ? `|${config}` : ""}`;
|
||||||
const { interpreter: engine, XWorker } = getDetails(
|
const { interpreter: engine, XWorker: Worker } = getDetails(
|
||||||
runtime,
|
runtime,
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
@@ -60,8 +57,8 @@ export const handleCustomType = (node) => {
|
|||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
engine.then((interpreter) => {
|
engine.then((interpreter) => {
|
||||||
if (!patched.has(id)) {
|
|
||||||
const module = create(defaultRegistry.get(runtime));
|
const module = create(defaultRegistry.get(runtime));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
onBeforeRun,
|
onBeforeRun,
|
||||||
onBeforeRunAsync,
|
onBeforeRunAsync,
|
||||||
@@ -73,6 +70,17 @@ export const handleCustomType = (node) => {
|
|||||||
codeAfterRunWorkerAsync,
|
codeAfterRunWorkerAsync,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
const hooks = {
|
||||||
|
beforeRun: codeBeforeRunWorker?.(),
|
||||||
|
beforeRunAsync: codeBeforeRunWorkerAsync?.(),
|
||||||
|
afterRun: codeAfterRunWorker?.(),
|
||||||
|
afterRunAsync: codeAfterRunWorkerAsync?.(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const XWorker = function XWorker(...args) {
|
||||||
|
return Worker.apply(hooks, args);
|
||||||
|
};
|
||||||
|
|
||||||
// These two loops mimic a `new Map(arrayContent)` without needing
|
// These two loops mimic a `new Map(arrayContent)` without needing
|
||||||
// the new Map overhead so that [name, [before, after]] can be easily destructured
|
// the new Map overhead so that [name, [before, after]] can be easily destructured
|
||||||
// and new sync or async patches become easy to add (when the logic is the same).
|
// and new sync or async patches become easy to add (when the logic is the same).
|
||||||
@@ -84,11 +92,7 @@ export const handleCustomType = (node) => {
|
|||||||
const method = module[name];
|
const method = module[name];
|
||||||
module[name] = function (interpreter, code) {
|
module[name] = function (interpreter, code) {
|
||||||
if (before) before.call(this, resolved, node);
|
if (before) before.call(this, resolved, node);
|
||||||
const result = method.call(
|
const result = method.call(this, interpreter, code);
|
||||||
this,
|
|
||||||
interpreter,
|
|
||||||
code,
|
|
||||||
);
|
|
||||||
if (after) after.call(this, resolved, node);
|
if (after) after.call(this, resolved, node);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@@ -100,30 +104,17 @@ export const handleCustomType = (node) => {
|
|||||||
]) {
|
]) {
|
||||||
const method = module[name];
|
const method = module[name];
|
||||||
module[name] = async function (interpreter, code) {
|
module[name] = async function (interpreter, code) {
|
||||||
if (before)
|
if (before) await before.call(this, resolved, node);
|
||||||
await before.call(this, resolved, node);
|
|
||||||
const result = await method.call(
|
const result = await method.call(
|
||||||
this,
|
this,
|
||||||
interpreter,
|
interpreter,
|
||||||
code,
|
code,
|
||||||
);
|
);
|
||||||
if (after)
|
if (after) await after.call(this, resolved, node);
|
||||||
await after.call(this, resolved, node);
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup XWorker hooks, allowing strings to be forwarded to the worker
|
|
||||||
// whenever it's created, as functions can't possibly be serialized
|
|
||||||
// unless these are pure with no outer scope access (or globals vars)
|
|
||||||
// so that making it strings disambiguate about their running context.
|
|
||||||
workerHooks.set(XWorker, {
|
|
||||||
beforeRun: codeBeforeRunWorker,
|
|
||||||
beforeRunAsync: codeBeforeRunWorkerAsync,
|
|
||||||
afterRun: codeAfterRunWorker,
|
|
||||||
afterRunAsync: codeAfterRunWorkerAsync,
|
|
||||||
});
|
|
||||||
|
|
||||||
module.setGlobal(interpreter, "XWorker", XWorker);
|
module.setGlobal(interpreter, "XWorker", XWorker);
|
||||||
|
|
||||||
const resolved = {
|
const resolved = {
|
||||||
@@ -136,11 +127,9 @@ export const handleCustomType = (node) => {
|
|||||||
runAsync: module.runAsync.bind(module, interpreter),
|
runAsync: module.runAsync.bind(module, interpreter),
|
||||||
};
|
};
|
||||||
|
|
||||||
patched.set(id, resolved);
|
|
||||||
resolve(resolved);
|
resolve(resolved);
|
||||||
}
|
|
||||||
|
|
||||||
onRuntimeReady?.(patched.get(id), node);
|
onRuntimeReady?.(resolved, node);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
195
pyscript.core/esm/custom/pyscript.js
Normal file
195
pyscript.core/esm/custom/pyscript.js
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import "@ungap/with-resolvers";
|
||||||
|
import { $ } from "basic-devtools";
|
||||||
|
|
||||||
|
import { define } from "../index.js";
|
||||||
|
import { queryTarget } from "../script-handler.js";
|
||||||
|
import { defineProperty } from "../utils.js";
|
||||||
|
import { getText } from "../fetch-utils.js";
|
||||||
|
|
||||||
|
// TODO: should this utility be in core instead?
|
||||||
|
import { robustFetch as fetch } from "./pyscript/fetch.js";
|
||||||
|
|
||||||
|
// append ASAP CSS to avoid showing content
|
||||||
|
document.head.appendChild(document.createElement("style")).textContent = `
|
||||||
|
py-script, py-config {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// create a unique identifier when/if needed
|
||||||
|
let id = 0;
|
||||||
|
const getID = (prefix = "py") => `${prefix}-${id++}`;
|
||||||
|
|
||||||
|
// find the shared config for all py-script elements
|
||||||
|
let config;
|
||||||
|
let pyConfig = $("py-config");
|
||||||
|
if (pyConfig) config = pyConfig.getAttribute("src") || pyConfig.textContent;
|
||||||
|
else {
|
||||||
|
pyConfig = $('script[type="py"]');
|
||||||
|
config = pyConfig?.getAttribute("config");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^https?:\/\//.test(config)) config = await fetch(config).then(getText);
|
||||||
|
|
||||||
|
// generic helper to disambiguate between custom element and script
|
||||||
|
const isScript = (element) => element.tagName === "SCRIPT";
|
||||||
|
|
||||||
|
// helper for all script[type="py"] out there
|
||||||
|
const before = (script) => {
|
||||||
|
defineProperty(document, "currentScript", {
|
||||||
|
configurable: true,
|
||||||
|
get: () => script,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const after = () => {
|
||||||
|
delete document.currentScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a generic DOM Element, tries to fetch the 'src' attribute, if present.
|
||||||
|
* It either throws an error if the 'src' can't be fetched or it returns a fallback
|
||||||
|
* content as source.
|
||||||
|
*/
|
||||||
|
const fetchSource = async (tag) => {
|
||||||
|
if (tag.hasAttribute("src")) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(tag.getAttribute("src"));
|
||||||
|
return response.then(getText);
|
||||||
|
} catch (error) {
|
||||||
|
// TODO _createAlertBanner(err) instead ?
|
||||||
|
alert(error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tag.textContent;
|
||||||
|
};
|
||||||
|
|
||||||
|
// common life-cycle handlers for any node
|
||||||
|
const bootstrapNodeAndPlugins = (pyodide, element, callback, hook) => {
|
||||||
|
if (isScript(element)) callback(element);
|
||||||
|
for (const fn of hooks[hook]) fn(pyodide, element);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addDisplay = (element) => {
|
||||||
|
const id = isScript(element) ? element.target.id : element.id;
|
||||||
|
return `
|
||||||
|
# this code is just for demo purpose but the basics work
|
||||||
|
def _display(what, target="${id}", append=True):
|
||||||
|
from js import document
|
||||||
|
element = document.getElementById(target)
|
||||||
|
element.textContent = what
|
||||||
|
display = _display
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// define the module as both `<script type="py">` and `<py-script>`
|
||||||
|
define("py", {
|
||||||
|
config,
|
||||||
|
env: "py-script",
|
||||||
|
interpreter: "pyodide",
|
||||||
|
codeBeforeRunWorker() {
|
||||||
|
const { codeBeforeRunWorker: set } = hooks;
|
||||||
|
const prefix = 'print("codeBeforeRunWorker")';
|
||||||
|
return [prefix].concat(...set).join("\n");
|
||||||
|
},
|
||||||
|
codeAfterRunWorker() {
|
||||||
|
const { codeAfterRunWorker: set } = hooks;
|
||||||
|
const prefix = 'print("codeAfterRunWorker")';
|
||||||
|
return [prefix].concat(...set).join("\n");
|
||||||
|
},
|
||||||
|
onBeforeRun(pyodide, element) {
|
||||||
|
bootstrapNodeAndPlugins(pyodide, element, before, "onBeforeRun");
|
||||||
|
pyodide.interpreter.runPython(addDisplay(element));
|
||||||
|
},
|
||||||
|
onBeforeRunAync(pyodide, element) {
|
||||||
|
pyodide.interpreter.runPython(addDisplay(element));
|
||||||
|
bootstrapNodeAndPlugins(
|
||||||
|
pyodide,
|
||||||
|
element,
|
||||||
|
before,
|
||||||
|
"onBeforeRunAync",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onAfterRun(pyodide, element) {
|
||||||
|
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRun");
|
||||||
|
},
|
||||||
|
onAfterRunAsync(pyodide, element) {
|
||||||
|
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRunAsync");
|
||||||
|
},
|
||||||
|
async onRuntimeReady(pyodide, element) {
|
||||||
|
// allows plugins to do whatever they want with the element
|
||||||
|
// before regular stuff happens in here
|
||||||
|
for (const callback of hooks.onRuntimeReady)
|
||||||
|
callback(pyodide, element);
|
||||||
|
if (isScript(element)) {
|
||||||
|
const {
|
||||||
|
attributes: { async: isAsync, target },
|
||||||
|
} = element;
|
||||||
|
const hasTarget = !!target?.value;
|
||||||
|
const show = hasTarget
|
||||||
|
? queryTarget(target.value)
|
||||||
|
: document.createElement("script-py");
|
||||||
|
|
||||||
|
if (!hasTarget) element.after(show);
|
||||||
|
if (!show.id) show.id = getID();
|
||||||
|
|
||||||
|
// allows the code to retrieve the target element via
|
||||||
|
// document.currentScript.target if needed
|
||||||
|
defineProperty(element, "target", { value: show });
|
||||||
|
|
||||||
|
pyodide[`run${isAsync ? "Async" : ""}`](
|
||||||
|
await fetchSource(element),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// resolve PyScriptElement to allow connectedCallback
|
||||||
|
element._pyodide.resolve(pyodide);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class PyScriptElement extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
if (!super().id) this.id = getID();
|
||||||
|
this._pyodide = Promise.withResolvers();
|
||||||
|
this.srcCode = "";
|
||||||
|
this.executed = false;
|
||||||
|
}
|
||||||
|
async connectedCallback() {
|
||||||
|
if (!this.executed) {
|
||||||
|
this.executed = true;
|
||||||
|
const { run } = await this._pyodide.promise;
|
||||||
|
this.srcCode = await fetchSource(this);
|
||||||
|
this.textContent = "";
|
||||||
|
const result = run(this.srcCode);
|
||||||
|
if (!this.textContent && result) this.textContent = result;
|
||||||
|
this.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("py-script", PyScriptElement);
|
||||||
|
})();
|
||||||
|
|
||||||
|
export const hooks = {
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onBeforeRun: new Set(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onBeforeRunAync: new Set(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onAfterRun: new Set(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onAfterRunAsync: new Set(),
|
||||||
|
/** @type {Set<function>} */
|
||||||
|
onRuntimeReady: new Set(),
|
||||||
|
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeBeforeRunWorker: new Set(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeBeforeRunWorkerAsync: new Set(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeAfterRunWorker: new Set(),
|
||||||
|
/** @type {Set<string>} */
|
||||||
|
codeAfterRunWorkerAsync: new Set(),
|
||||||
|
};
|
||||||
80
pyscript.core/esm/custom/pyscript/exceptions.js
Normal file
80
pyscript.core/esm/custom/pyscript/exceptions.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
const CLOSEBUTTON = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill="currentColor" width="12px"><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These error codes are used to identify the type of error that occurred.
|
||||||
|
* @see https://docs.pyscript.net/latest/reference/exceptions.html?highlight=errors
|
||||||
|
*/
|
||||||
|
export const ErrorCode = {
|
||||||
|
GENERIC: "PY0000", // Use this only for development then change to a more specific error code
|
||||||
|
FETCH_ERROR: "PY0001",
|
||||||
|
FETCH_NAME_ERROR: "PY0002",
|
||||||
|
// Currently these are created depending on error code received from fetching
|
||||||
|
FETCH_UNAUTHORIZED_ERROR: "PY0401",
|
||||||
|
FETCH_FORBIDDEN_ERROR: "PY0403",
|
||||||
|
FETCH_NOT_FOUND_ERROR: "PY0404",
|
||||||
|
FETCH_SERVER_ERROR: "PY0500",
|
||||||
|
FETCH_UNAVAILABLE_ERROR: "PY0503",
|
||||||
|
BAD_CONFIG: "PY1000",
|
||||||
|
MICROPIP_INSTALL_ERROR: "PY1001",
|
||||||
|
BAD_PLUGIN_FILE_EXTENSION: "PY2000",
|
||||||
|
NO_DEFAULT_EXPORT: "PY2001",
|
||||||
|
TOP_LEVEL_AWAIT: "PY9000",
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UserError extends Error {
|
||||||
|
constructor(errorCode, message = "", messageType = "text") {
|
||||||
|
super(`(${errorCode}): ${message}`);
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.messageType = messageType;
|
||||||
|
this.name = "UserError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FetchError extends UserError {
|
||||||
|
constructor(errorCode, message) {
|
||||||
|
super(errorCode, message);
|
||||||
|
this.name = "FetchError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InstallError extends UserError {
|
||||||
|
constructor(errorCode, message) {
|
||||||
|
super(errorCode, message);
|
||||||
|
this.name = "InstallError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _createAlertBanner(
|
||||||
|
message,
|
||||||
|
level,
|
||||||
|
messageType = "text",
|
||||||
|
logMessage = true,
|
||||||
|
) {
|
||||||
|
switch (`log-${level}-${logMessage}`) {
|
||||||
|
case "log-error-true":
|
||||||
|
console.error(message);
|
||||||
|
break;
|
||||||
|
case "log-warning-true":
|
||||||
|
console.warn(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = messageType === "html" ? "innerHTML" : "textContent";
|
||||||
|
const banner = Object.assign(document.createElement("div"), {
|
||||||
|
className: `alert-banner py-${level}`,
|
||||||
|
[content]: message,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (level === "warning") {
|
||||||
|
const closeButton = Object.assign(document.createElement("button"), {
|
||||||
|
id: "alert-close-button",
|
||||||
|
innerHTML: CLOSEBUTTON,
|
||||||
|
});
|
||||||
|
|
||||||
|
banner.appendChild(closeButton).addEventListener("click", () => {
|
||||||
|
banner.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.prepend(banner);
|
||||||
|
}
|
||||||
63
pyscript.core/esm/custom/pyscript/fetch.js
Normal file
63
pyscript.core/esm/custom/pyscript/fetch.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { FetchError, ErrorCode } from "./exceptions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a fetch wrapper that handles any non 200 responses and throws a
|
||||||
|
* FetchError with the right ErrorCode. This is useful because our FetchError
|
||||||
|
* will automatically create an alert banner.
|
||||||
|
*
|
||||||
|
* @param {string} url - URL to fetch
|
||||||
|
* @param {Request} [options] - options to pass to fetch
|
||||||
|
* @returns {Promise<Response>}
|
||||||
|
*/
|
||||||
|
export async function robustFetch(url, options) {
|
||||||
|
let response;
|
||||||
|
|
||||||
|
// Note: We need to wrap fetch into a try/catch block because fetch
|
||||||
|
// throws a TypeError if the URL is invalid such as http://blah.blah
|
||||||
|
try {
|
||||||
|
response = await fetch(url, options);
|
||||||
|
} catch (err) {
|
||||||
|
const error = err;
|
||||||
|
let errMsg;
|
||||||
|
if (url.startsWith("http")) {
|
||||||
|
errMsg =
|
||||||
|
`Fetching from URL ${url} failed with error ` +
|
||||||
|
`'${error.message}'. Are your filename and path correct?`;
|
||||||
|
} else {
|
||||||
|
errMsg = `PyScript: Access to local files
|
||||||
|
(using [[fetch]] configurations in <py-config>)
|
||||||
|
is not available when directly opening a HTML file;
|
||||||
|
you must use a webserver to serve the additional files.
|
||||||
|
See <a style="text-decoration: underline;" href="https://github.com/pyscript/pyscript/issues/257#issuecomment-1119595062">this reference</a>
|
||||||
|
on starting a simple webserver with Python.
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
throw new FetchError(ErrorCode.FETCH_ERROR, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that response.ok is true for 200-299 responses
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorMsg = `Fetching from URL ${url} failed with error ${response.status} (${response.statusText}). Are your filename and path correct?`;
|
||||||
|
switch (response.status) {
|
||||||
|
case 404:
|
||||||
|
throw new FetchError(ErrorCode.FETCH_NOT_FOUND_ERROR, errorMsg);
|
||||||
|
case 401:
|
||||||
|
throw new FetchError(
|
||||||
|
ErrorCode.FETCH_UNAUTHORIZED_ERROR,
|
||||||
|
errorMsg,
|
||||||
|
);
|
||||||
|
case 403:
|
||||||
|
throw new FetchError(ErrorCode.FETCH_FORBIDDEN_ERROR, errorMsg);
|
||||||
|
case 500:
|
||||||
|
throw new FetchError(ErrorCode.FETCH_SERVER_ERROR, errorMsg);
|
||||||
|
case 503:
|
||||||
|
throw new FetchError(
|
||||||
|
ErrorCode.FETCH_UNAVAILABLE_ERROR,
|
||||||
|
errorMsg,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new FetchError(ErrorCode.FETCH_ERROR, errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ import xworker from "./worker/class.js";
|
|||||||
import { handle } from "./script-handler.js";
|
import { handle } from "./script-handler.js";
|
||||||
import { assign } from "./utils.js";
|
import { assign } from "./utils.js";
|
||||||
import { selectors, prefixes } from "./interpreters.js";
|
import { selectors, prefixes } from "./interpreters.js";
|
||||||
import { CUSTOM_SELECTORS, handleCustomType } from "./custom-types.js";
|
import { CUSTOM_SELECTORS, handleCustomType } from "./custom.js";
|
||||||
import { listener, addAllListeners } from "./listeners.js";
|
import { listener, addAllListeners } from "./listeners.js";
|
||||||
|
|
||||||
export { define, whenDefined } from "./custom-types.js";
|
export { define, whenDefined } from "./custom.js";
|
||||||
export const XWorker = xworker();
|
export const XWorker = xworker();
|
||||||
|
|
||||||
const INTERPRETER_SELECTORS = selectors.join(",");
|
const INTERPRETER_SELECTORS = selectors.join(",");
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ export const run = (interpreter, code) => interpreter.runPython(clean(code));
|
|||||||
export const runAsync = (interpreter, code) =>
|
export const runAsync = (interpreter, code) =>
|
||||||
interpreter.runPythonAsync(clean(code));
|
interpreter.runPythonAsync(clean(code));
|
||||||
|
|
||||||
|
export const setGlobal = (interpreter, name, value) =>
|
||||||
|
interpreter.globals.set(name, value);
|
||||||
|
|
||||||
|
export const deleteGlobal = (interpreter, name) =>
|
||||||
|
interpreter.globals.delete(name);
|
||||||
|
|
||||||
export const writeFile = ({ FS }, path, buffer) =>
|
export const writeFile = ({ FS }, path, buffer) =>
|
||||||
writeFileUtil(FS, path, buffer);
|
writeFileUtil(FS, path, buffer);
|
||||||
/* c8 ignore stop */
|
/* c8 ignore stop */
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { fetchPaths, stdio } from "./_utils.js";
|
import { fetchPaths, stdio } from "./_utils.js";
|
||||||
import { run, runAsync, writeFile } from "./_python.js";
|
import {
|
||||||
|
run,
|
||||||
|
runAsync,
|
||||||
|
setGlobal,
|
||||||
|
deleteGlobal,
|
||||||
|
writeFile,
|
||||||
|
} from "./_python.js";
|
||||||
|
|
||||||
const type = "micropython";
|
const type = "micropython";
|
||||||
|
|
||||||
@@ -7,7 +13,7 @@ const type = "micropython";
|
|||||||
/* c8 ignore start */
|
/* c8 ignore start */
|
||||||
export default {
|
export default {
|
||||||
type,
|
type,
|
||||||
module: (version = "1.20.0-239") =>
|
module: (version = "1.20.0-253") =>
|
||||||
`https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${version}/micropython.mjs`,
|
`https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${version}/micropython.mjs`,
|
||||||
async engine({ loadMicroPython }, config, url) {
|
async engine({ loadMicroPython }, config, url) {
|
||||||
const { stderr, stdout, get } = stdio();
|
const { stderr, stdout, get } = stdio();
|
||||||
@@ -16,16 +22,8 @@ export default {
|
|||||||
if (config.fetch) await fetchPaths(this, runtime, config.fetch);
|
if (config.fetch) await fetchPaths(this, runtime, config.fetch);
|
||||||
return runtime;
|
return runtime;
|
||||||
},
|
},
|
||||||
setGlobal(interpreter, name, value) {
|
setGlobal,
|
||||||
const id = `__pyscript_${this.type}_${name}`;
|
deleteGlobal,
|
||||||
globalThis[id] = value;
|
|
||||||
this.run(interpreter, `from js import ${id};${name}=${id};`);
|
|
||||||
},
|
|
||||||
deleteGlobal(interpreter, name) {
|
|
||||||
const id = `__pyscript_${this.type}_${name}`;
|
|
||||||
this.run(interpreter, `del ${id};del ${name}`);
|
|
||||||
delete globalThis[id];
|
|
||||||
},
|
|
||||||
run,
|
run,
|
||||||
runAsync,
|
runAsync,
|
||||||
writeFile,
|
writeFile,
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { fetchPaths, stdio } from "./_utils.js";
|
import { fetchPaths, stdio } from "./_utils.js";
|
||||||
import { run, runAsync, writeFile } from "./_python.js";
|
import {
|
||||||
|
run,
|
||||||
|
runAsync,
|
||||||
|
setGlobal,
|
||||||
|
deleteGlobal,
|
||||||
|
writeFile,
|
||||||
|
} from "./_python.js";
|
||||||
|
|
||||||
const type = "pyodide";
|
const type = "pyodide";
|
||||||
|
|
||||||
@@ -24,12 +30,8 @@ export default {
|
|||||||
}
|
}
|
||||||
return interpreter;
|
return interpreter;
|
||||||
},
|
},
|
||||||
setGlobal(interpreter, name, value) {
|
setGlobal,
|
||||||
interpreter.globals.set(name, value);
|
deleteGlobal,
|
||||||
},
|
|
||||||
deleteGlobal(interpreter, name) {
|
|
||||||
interpreter.globals.delete(name);
|
|
||||||
},
|
|
||||||
run,
|
run,
|
||||||
runAsync,
|
runAsync,
|
||||||
writeFile,
|
writeFile,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const getRoot = (script) => {
|
|||||||
return parent;
|
return parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryTarget = (script, idOrSelector) => {
|
export const queryTarget = (script, idOrSelector) => {
|
||||||
const root = getRoot(script);
|
const root = getRoot(script);
|
||||||
return root.getElementById(idOrSelector) || $(idOrSelector, root);
|
return root.getElementById(idOrSelector) || $(idOrSelector, root);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +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 * as JSON from "@ungap/structured-clone/json";
|
||||||
|
import coincident from "coincident/window";
|
||||||
|
|
||||||
import { create } from "../utils.js";
|
import { create } from "../utils.js";
|
||||||
import { registry } from "../interpreters.js";
|
import { registry } from "../interpreters.js";
|
||||||
@@ -37,9 +38,15 @@ const add = (type, fn) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { proxy: sync, window, isWindowProxy } = coincident(self, JSON);
|
||||||
|
|
||||||
const xworker = {
|
const xworker = {
|
||||||
// allows synchronous utilities between this worker and the main thread
|
// allows synchronous utilities between this worker and the main thread
|
||||||
sync: coincident(self),
|
sync,
|
||||||
|
// allow access to the main thread world
|
||||||
|
window,
|
||||||
|
// allow introspection for foreign (main thread) refrences
|
||||||
|
isWindowProxy,
|
||||||
// standard worker related events / features
|
// standard worker related events / features
|
||||||
onerror() {},
|
onerror() {},
|
||||||
onmessage() {},
|
onmessage() {},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import coincident from "coincident/structured";
|
import * as JSON from "@ungap/structured-clone/json";
|
||||||
|
import coincident from "coincident/window";
|
||||||
import xworker from "./xworker.js";
|
import xworker from "./xworker.js";
|
||||||
import { assign, defineProperties, absoluteURL } from "../utils.js";
|
import { assign, defineProperties, absoluteURL } from "../utils.js";
|
||||||
import { getText } from "../fetch-utils.js";
|
import { getText } from "../fetch-utils.js";
|
||||||
import workerHooks from "./hooks.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} WorkerOptions custom configuration
|
* @typedef {Object} WorkerOptions custom configuration
|
||||||
@@ -19,9 +19,9 @@ export default (...args) =>
|
|||||||
* @returns {Worker}
|
* @returns {Worker}
|
||||||
*/
|
*/
|
||||||
function XWorker(url, options) {
|
function XWorker(url, options) {
|
||||||
const hooks = workerHooks.get(XWorker);
|
|
||||||
const worker = xworker();
|
const worker = xworker();
|
||||||
const { postMessage } = worker;
|
const { postMessage } = worker;
|
||||||
|
const hooks = this instanceof XWorker ? void 0 : this;
|
||||||
if (args.length) {
|
if (args.length) {
|
||||||
const [type, version] = args;
|
const [type, version] = args;
|
||||||
options = assign({}, options || { type, version });
|
options = assign({}, options || { type, version });
|
||||||
@@ -39,7 +39,7 @@ export default (...args) =>
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
sync: {
|
sync: {
|
||||||
value: coincident(worker),
|
value: coincident(worker, JSON).proxy,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export default new WeakMap();
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"imports": {
|
"imports": {
|
||||||
"http://pyodide": "./test/mocked/pyodide.mjs",
|
"http://pyodide": "./test/mocked/pyodide.mjs",
|
||||||
"https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.mjs": "./test/mocked/pyodide.mjs",
|
"https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.mjs": "./test/mocked/pyodide.mjs",
|
||||||
"https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@1.20.0-239/micropython.mjs": "./test/mocked/micropython.mjs",
|
"https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@1.20.0-253/micropython.mjs": "./test/mocked/micropython.mjs",
|
||||||
"https://cdn.jsdelivr.net/npm/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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
pyscript.core/package-lock.json
generated
87
pyscript.core/package-lock.json
generated
@@ -9,9 +9,10 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"coincident": "^0.4.1"
|
"coincident": "^0.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@node-loader/import-maps": "^1.1.0",
|
"@node-loader/import-maps": "^1.1.0",
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.3",
|
"@rollup/plugin-terser": "^0.4.3",
|
||||||
"ascjs": "^5.0.1",
|
"ascjs": "^5.0.1",
|
||||||
"c8": "^8.0.0",
|
"c8": "^8.0.0",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.43.0",
|
||||||
"linkedom": "^0.14.26",
|
"linkedom": "^0.14.26",
|
||||||
"rollup": "^3.25.1",
|
"rollup": "^3.25.1",
|
||||||
"static-handler": "^0.4.1",
|
"static-handler": "^0.4.2",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -92,9 +93,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
|
||||||
"integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==",
|
"integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
@@ -348,9 +349,9 @@
|
|||||||
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
|
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.8.2",
|
"version": "8.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
|
||||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
"integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@@ -534,9 +535,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/coincident": {
|
"node_modules/coincident": {
|
||||||
"version": "0.4.1",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.7.2.tgz",
|
||||||
"integrity": "sha512-CiHZVx/eUano0G+sSc0S0pXTBH51q0W7pMw1ScRa57BgfBw8l19pMW2Z6sVDHjOih5c9NYrP0budO8SslJv2sA==",
|
"integrity": "sha512-/hNcCDfousAW04kxOeuwzrM+gIQZpFDCYKXvDex/DJxvIcDL57WM32zGO9t8xT34cpBJUl0GNEclpaOx7/rk4A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ungap/structured-clone": "^1.2.0"
|
"@ungap/structured-clone": "^1.2.0"
|
||||||
}
|
}
|
||||||
@@ -764,15 +765,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
|
||||||
"integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==",
|
"integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.4.0",
|
"@eslint-community/regexpp": "^4.4.0",
|
||||||
"@eslint/eslintrc": "^2.0.3",
|
"@eslint/eslintrc": "^2.0.3",
|
||||||
"@eslint/js": "8.42.0",
|
"@eslint/js": "8.43.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.10",
|
"@humanwhocodes/config-array": "^0.11.10",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
@@ -1796,12 +1797,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/static-handler": {
|
"node_modules/static-handler": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.2.tgz",
|
||||||
"integrity": "sha512-atl+UofbqCECS4Ag1ZZIjC606I1I40CuDsdq4YQ5Vq3vTsUbvqVaNG4evFsKktlnPcEm217vBUSEFKyi9lsJ8Q==",
|
"integrity": "sha512-Szbk521mneb5npxg3SEyoufsHr2osAzxMy71W2zFCzLB8wLhHYvKUDCMkLY6imi+fIqkpfas3rzXGBQf99aeEA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-types": "^2.1.34"
|
"mime-types": "^2.1.35"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"static-handler": "static-handler.cjs"
|
"static-handler": "static-handler.cjs"
|
||||||
@@ -1873,9 +1874,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.18.0",
|
"version": "5.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz",
|
||||||
"integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==",
|
"integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
@@ -2118,9 +2119,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@eslint/js": {
|
"@eslint/js": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
|
||||||
"integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==",
|
"integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@humanwhocodes/config-array": {
|
"@humanwhocodes/config-array": {
|
||||||
@@ -2306,9 +2307,9 @@
|
|||||||
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
|
"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.8.2",
|
"version": "8.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
|
||||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
"integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"acorn-jsx": {
|
"acorn-jsx": {
|
||||||
@@ -2447,9 +2448,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"coincident": {
|
"coincident": {
|
||||||
"version": "0.4.1",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/coincident/-/coincident-0.7.2.tgz",
|
||||||
"integrity": "sha512-CiHZVx/eUano0G+sSc0S0pXTBH51q0W7pMw1ScRa57BgfBw8l19pMW2Z6sVDHjOih5c9NYrP0budO8SslJv2sA==",
|
"integrity": "sha512-/hNcCDfousAW04kxOeuwzrM+gIQZpFDCYKXvDex/DJxvIcDL57WM32zGO9t8xT34cpBJUl0GNEclpaOx7/rk4A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@ungap/structured-clone": "^1.2.0"
|
"@ungap/structured-clone": "^1.2.0"
|
||||||
}
|
}
|
||||||
@@ -2615,15 +2616,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "8.42.0",
|
"version": "8.43.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
|
||||||
"integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==",
|
"integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.4.0",
|
"@eslint-community/regexpp": "^4.4.0",
|
||||||
"@eslint/eslintrc": "^2.0.3",
|
"@eslint/eslintrc": "^2.0.3",
|
||||||
"@eslint/js": "8.42.0",
|
"@eslint/js": "8.43.0",
|
||||||
"@humanwhocodes/config-array": "^0.11.10",
|
"@humanwhocodes/config-array": "^0.11.10",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
"@nodelib/fs.walk": "^1.2.8",
|
"@nodelib/fs.walk": "^1.2.8",
|
||||||
@@ -3363,12 +3364,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"static-handler": {
|
"static-handler": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/static-handler/-/static-handler-0.4.2.tgz",
|
||||||
"integrity": "sha512-atl+UofbqCECS4Ag1ZZIjC606I1I40CuDsdq4YQ5Vq3vTsUbvqVaNG4evFsKktlnPcEm217vBUSEFKyi9lsJ8Q==",
|
"integrity": "sha512-Szbk521mneb5npxg3SEyoufsHr2osAzxMy71W2zFCzLB8wLhHYvKUDCMkLY6imi+fIqkpfas3rzXGBQf99aeEA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-types": "^2.1.34"
|
"mime-types": "^2.1.35"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
@@ -3413,9 +3414,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"terser": {
|
"terser": {
|
||||||
"version": "5.18.0",
|
"version": "5.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz",
|
||||||
"integrity": "sha512-pdL757Ig5a0I+owA42l6tIuEycRuM7FPY4n62h44mRLRfnOxJkkOHd6i89dOpwZlpF6JXBwaAHF6yWzFrt+QyA==",
|
"integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "@pyscript/core",
|
"name": "@pyscript/core",
|
||||||
"version": "0.0.0",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"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 --corp .",
|
"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:core && npm run rollup:pyscript && 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:core": "rollup --config rollup/core.config.js",
|
||||||
|
"rollup:pyscript": "rollup --config rollup/pyscript.config.js",
|
||||||
"rollup:xworker": "rollup --config rollup/xworker.config.js",
|
"rollup:xworker": "rollup --config rollup/xworker.config.js",
|
||||||
"test": "c8 --100 node --experimental-loader @node-loader/import-maps test/index.js ",
|
"test": "c8 --100 node --experimental-loader @node-loader/import-maps test/index.js ",
|
||||||
"test:html": "npm run test && c8 report -r html",
|
"test:html": "npm run test && c8 report -r html",
|
||||||
"coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info",
|
"coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info",
|
||||||
"size": "npm run size:module && npm run size:worker",
|
"size": "npm run size:module && npm run size:worker",
|
||||||
"size:module": "echo module is $(cat min.js | brotli | wc -c) bytes once compressed",
|
"size:module": "echo module is $(cat core.js | brotli | wc -c) bytes once compressed",
|
||||||
"size:worker": "echo worker is $(cat esm/worker/xworker.js | brotli | wc -c) bytes once compressed",
|
"size:worker": "echo worker is $(cat esm/worker/xworker.js | brotli | wc -c) bytes once compressed",
|
||||||
"ts": "tsc -p ."
|
"ts": "tsc -p ."
|
||||||
},
|
},
|
||||||
@@ -27,10 +28,10 @@
|
|||||||
"@rollup/plugin-terser": "^0.4.3",
|
"@rollup/plugin-terser": "^0.4.3",
|
||||||
"ascjs": "^5.0.1",
|
"ascjs": "^5.0.1",
|
||||||
"c8": "^8.0.0",
|
"c8": "^8.0.0",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.43.0",
|
||||||
"linkedom": "^0.14.26",
|
"linkedom": "^0.14.26",
|
||||||
"rollup": "^3.25.1",
|
"rollup": "^3.25.1",
|
||||||
"static-handler": "^0.4.1",
|
"static-handler": "^0.4.2",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"module": "./esm/index.js",
|
"module": "./esm/index.js",
|
||||||
@@ -43,13 +44,14 @@
|
|||||||
},
|
},
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"unpkg": "min.js",
|
"unpkg": "core.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ungap/structured-clone": "^1.2.0",
|
||||||
"@ungap/with-resolvers": "^0.1.0",
|
"@ungap/with-resolvers": "^0.1.0",
|
||||||
"basic-devtools": "^0.1.6",
|
"basic-devtools": "^0.1.6",
|
||||||
"coincident": "^0.4.1"
|
"coincident": "^0.7.2"
|
||||||
},
|
},
|
||||||
"worker": {
|
"worker": {
|
||||||
"blob": "sha256-obDch1OaxOZKhaCpSW9vEAHJk01N+9kYvSiGkNmeJRU="
|
"blob": "sha256-llcU6BJSNpU+9XN3CXzTrR1Js0KQaXygnnKiD039CTQ="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
pyscript.core/pyscript.js
Normal file
2
pyscript.core/pyscript.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
// This file generates /min.js minified version of the module, which is
|
// This file generates /core.js minified version of the module, which is
|
||||||
// the default exported as npm entry.
|
// the default exported as npm entry.
|
||||||
|
|
||||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||||
@@ -13,6 +13,6 @@ export default {
|
|||||||
plugins: process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
|
plugins: process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
|
||||||
output: {
|
output: {
|
||||||
esModule: true,
|
esModule: true,
|
||||||
file: "./min.js",
|
file: "./core.js",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
18
pyscript.core/rollup/pyscript.config.js
Normal file
18
pyscript.core/rollup/pyscript.config.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// This file generates /core.js minified version of the module, which is
|
||||||
|
// the default exported as npm entry.
|
||||||
|
|
||||||
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||||
|
import terser from "@rollup/plugin-terser";
|
||||||
|
|
||||||
|
import { createRequire } from "node:module";
|
||||||
|
|
||||||
|
createRequire(import.meta.url)("./build_xworker.cjs");
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: "./esm/custom/pyscript.js",
|
||||||
|
plugins: process.env.NO_MIN ? [nodeResolve()] : [nodeResolve(), terser()],
|
||||||
|
output: {
|
||||||
|
esModule: true,
|
||||||
|
file: "./pyscript.js",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// This file generates /min.js minified version of the module, which is
|
// This file generates /core.js minified version of the module, which is
|
||||||
// the default exported as npm entry.
|
// the default exported as npm entry.
|
||||||
|
|
||||||
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide" async>
|
<script type="pyodide" async>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="micropython">
|
<script type="micropython">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>python</title>
|
<title>python</title>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="micropython" config="./fetch.toml">
|
<script type="micropython" config="./fetch.toml">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide">
|
<script type="pyodide">
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>python</title>
|
<title>python</title>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide">
|
<script type="pyodide">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide" version="0.23.2">
|
<script type="pyodide" version="0.23.2">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide" config="./config.toml">
|
<script type="pyodide" config="./config.toml">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide" config="./config.json">
|
<script type="pyodide" config="./config.json">
|
||||||
|
|||||||
@@ -46,6 +46,26 @@ buf = io.BytesIO()
|
|||||||
plt.savefig(buf, format="png")
|
plt.savefig(buf, format="png")
|
||||||
buf.seek(0)
|
buf.seek(0)
|
||||||
|
|
||||||
js.xworker.postMessage(
|
# how it was (including main thread counter part)
|
||||||
"data:image/png;base64," + base64.b64encode(buf.read()).decode("UTF-8")
|
# js.xworker.postMessage(
|
||||||
)
|
# "data:image/png;base64," + base64.b64encode(buf.read()).decode("UTF-8")
|
||||||
|
# )
|
||||||
|
|
||||||
|
# how it is now via structured coincident/window
|
||||||
|
document = xworker.window.document
|
||||||
|
img = document.createElement("img")
|
||||||
|
img.style.transform = "scale(.5)"
|
||||||
|
img.src = "data:image/png;base64," + base64.b64encode(buf.read()).decode("UTF-8")
|
||||||
|
|
||||||
|
# document.body.append(img) fails for some reason I don't understand
|
||||||
|
# same would be for document.querySelector("#image").append(img)
|
||||||
|
# those would work in any JS counterpart though ... but for demo sake:
|
||||||
|
document.querySelector("#image").innerHTML = img.outerHTML
|
||||||
|
|
||||||
|
# about pyodide issue
|
||||||
|
print(xworker.window.Array(1, 2)) # this works
|
||||||
|
xworker.window.console.log(1, 2) # this works too
|
||||||
|
# xworker.window.console.log(xworker.window.Array(1, 2)) # this doesn't
|
||||||
|
# xworker.window.console.log([1, 2]) # also this doesn't
|
||||||
|
|
||||||
|
# xworker.window.console.log(to_js({})) # this doesn't neither
|
||||||
|
|||||||
@@ -6,19 +6,23 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="micropython">
|
<script type="micropython">
|
||||||
def show_image(event):
|
|
||||||
from js import document
|
|
||||||
img = document.createElement("img")
|
|
||||||
img.style.transform = "scale(.5)"
|
|
||||||
img.src = event.data
|
|
||||||
document.body.appendChild(img)
|
|
||||||
|
|
||||||
w = XWorker('./matplot.py', **{'type': 'pyodide', 'config': './config.toml'})
|
w = XWorker('./matplot.py', **{'type': 'pyodide', 'config': './config.toml'})
|
||||||
w.onmessage = show_image
|
|
||||||
|
# xworker.window made the following completely unnecessary
|
||||||
|
|
||||||
|
# def show_image(event):
|
||||||
|
# from js import document
|
||||||
|
# img = document.createElement("img")
|
||||||
|
# img.style.transform = "scale(.5)"
|
||||||
|
# img.src = event.data
|
||||||
|
# document.querySelector("#image").append(img)
|
||||||
|
|
||||||
|
# w.onmessage = show_image
|
||||||
</script>
|
</script>
|
||||||
|
<div id="image"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="micropython">
|
<script type="micropython">
|
||||||
|
|||||||
@@ -6,4 +6,12 @@ export const loadMicroPython = () => ({
|
|||||||
python.target = document.currentScript.target;
|
python.target = document.currentScript.target;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
globals: {
|
||||||
|
set(name, value) {
|
||||||
|
globalThis[name] = value;
|
||||||
|
},
|
||||||
|
delete(name) {
|
||||||
|
delete globalThis[name];
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide">
|
<script type="pyodide">
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
|
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../node_modules/@ungap/structured-clone/esm/json.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{ "imports": { "@pyscript/core": "../../min.js" } }
|
{ "imports": { "@pyscript/core": "../../core.js" } }
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { define, whenDefined } from "@pyscript/core";
|
import { define, whenDefined } from "@pyscript/core";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="importmap">
|
<script type="importmap">
|
||||||
{ "imports": { "@pyscript/core": "../../min.js" } }
|
{ "imports": { "@pyscript/core": "../../core.js" } }
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { define } from "@pyscript/core";
|
import { define } from "@pyscript/core";
|
||||||
|
|||||||
@@ -3,24 +3,30 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<title>PyScript</title>
|
<title>PyScript Next</title>
|
||||||
<style>
|
<script type="importmap">{ "imports": { "@pyscript/element": "../../pyscript.js" } }</script>
|
||||||
py-script {
|
<script type="module">
|
||||||
display: none;
|
// how would PyScript plugins add their own behavior?
|
||||||
}
|
import { hooks } from "@pyscript/element";
|
||||||
</style>
|
let counter = 0;
|
||||||
<script type="importmap">
|
hooks.onBeforeRun.add((pyodide, { localName }) => {
|
||||||
{ "imports": { "@pyscript/core": "../../min.js" } }
|
// console.log(++counter, 'elements so far', localName, pyodide);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="py-script.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<fieldset>
|
||||||
|
<legend id="dary"></legend>
|
||||||
|
Something something about something ...
|
||||||
|
</fieldset>
|
||||||
<py-config>
|
<py-config>
|
||||||
[[fetch]]
|
[[fetch]]
|
||||||
from = "../"
|
from = "../"
|
||||||
to_folder = "./"
|
to_folder = "./"
|
||||||
files = ["a.py", "b.py"]
|
files = ["a.py", "b.py"]
|
||||||
</py-config>
|
</py-config>
|
||||||
|
|
||||||
|
<!-- <py-script next> -->
|
||||||
<py-script>
|
<py-script>
|
||||||
import js
|
import js
|
||||||
import a, b
|
import a, b
|
||||||
@@ -29,8 +35,24 @@
|
|||||||
'Hello Web!'
|
'Hello Web!'
|
||||||
</py-script>
|
</py-script>
|
||||||
<py-script>
|
<py-script>
|
||||||
|
# note the target is this element itself
|
||||||
|
display('second <py-script>')
|
||||||
|
</py-script>
|
||||||
|
<py-script>
|
||||||
|
# note this is late to the party simply because
|
||||||
|
# pyodide needs to be bootstrapped in the Worker too
|
||||||
XWorker('../a.py')
|
XWorker('../a.py')
|
||||||
'OK'
|
'OK'
|
||||||
</py-script>
|
</py-script>
|
||||||
|
|
||||||
|
<!-- <script type="py"> -->
|
||||||
|
<script type="py">
|
||||||
|
# not the target is inferred as companion element
|
||||||
|
display('first <script type="py">')
|
||||||
|
</script>
|
||||||
|
<script type="py">
|
||||||
|
# note the target here is different
|
||||||
|
display('second <script type="py">', target="dary")
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import { define } from "@pyscript/core";
|
|
||||||
|
|
||||||
// append ASAP CSS to avoid showing content
|
|
||||||
document.head.appendChild(document.createElement("style")).textContent = `
|
|
||||||
py-script, py-config {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
// create a unique identifier when/if needed
|
|
||||||
let id = 0;
|
|
||||||
const getID = (prefix = "py-script") => `${prefix}-${id++}`;
|
|
||||||
|
|
||||||
let bootstrap = true,
|
|
||||||
XWorker,
|
|
||||||
sharedRuntime;
|
|
||||||
const sharedPyodide = new Promise((resolve) => {
|
|
||||||
const pyConfig = document.querySelector("py-config");
|
|
||||||
const config = pyConfig?.getAttribute("src") || pyConfig?.textContent;
|
|
||||||
define("py", {
|
|
||||||
config,
|
|
||||||
interpreter: "pyodide",
|
|
||||||
codeBeforeRunWorker: `print('codeBeforeRunWorker')`,
|
|
||||||
codeAfterRunWorker: `print('codeAfterRunWorker')`,
|
|
||||||
onBeforeRun(pyodide, node) {
|
|
||||||
pyodide.interpreter.globals.set("XWorker", XWorker);
|
|
||||||
console.log("onBeforeRun", sharedRuntime === pyodide, node);
|
|
||||||
},
|
|
||||||
onAfterRun(pyodide, node) {
|
|
||||||
console.log("onAfterRun", sharedRuntime === pyodide, node);
|
|
||||||
},
|
|
||||||
async onRuntimeReady(pyodide) {
|
|
||||||
// bootstrap the shared runtime once
|
|
||||||
// as each node as plugin gets onRuntimeReady called once
|
|
||||||
// because no custom-element is strictly needed
|
|
||||||
if (bootstrap) {
|
|
||||||
bootstrap = false;
|
|
||||||
sharedRuntime = pyodide;
|
|
||||||
XWorker = pyodide.XWorker;
|
|
||||||
pyodide.io.stdout = (message) => {
|
|
||||||
console.log("🐍", pyodide.type, message);
|
|
||||||
};
|
|
||||||
// do any module / JS injection in here such as
|
|
||||||
// Element, display, and friends ... then:
|
|
||||||
resolve(pyodide);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/** @type {WeakSet<PyScriptElement>} */
|
|
||||||
const known = new WeakSet();
|
|
||||||
|
|
||||||
class PyScriptElement extends HTMLElement {
|
|
||||||
constructor() {
|
|
||||||
if (!super().id) this.id = getID();
|
|
||||||
}
|
|
||||||
async connectedCallback() {
|
|
||||||
if (!known.has(this)) {
|
|
||||||
known.add(this);
|
|
||||||
// sharedPyodide contains various helpers including run and runAsync
|
|
||||||
const { run } = await sharedPyodide;
|
|
||||||
// do any stuff needed to finalize this element bootstrap
|
|
||||||
// (i.e. check src attribute and so on)
|
|
||||||
this.replaceChildren(run(this.textContent) || "");
|
|
||||||
// reveal the node on the page
|
|
||||||
this.style.display = "block";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("py-script", PyScriptElement);
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python events</title>
|
<title>python events</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="pyodide">
|
<script type="pyodide">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>python</title>
|
<title>python</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script
|
<script
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
|
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../node_modules/@ungap/structured-clone/esm/json.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script defer src="./counter.js"></script>
|
<script defer src="./counter.js"></script>
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
customElements.define(
|
customElements.define(
|
||||||
"shadow-dom",
|
"shadow-dom",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
<script type="module" src="../min.js"></script>
|
<script type="module" src="../core.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<table cellspacing="2" cellpadding="2">
|
<table cellspacing="2" cellpadding="2">
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
|
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../node_modules/@ungap/structured-clone/esm/json.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js"
|
"@ungap/with-resolvers": "../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../node_modules/@ungap/structured-clone/esm/json.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
|
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../../node_modules/@ungap/structured-clone/esm/json.js",
|
||||||
"@pyscript/core": "../../esm/index.js"
|
"@pyscript/core": "../../esm/index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
{
|
{
|
||||||
"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",
|
"coincident/window": "../../node_modules/coincident/window.js",
|
||||||
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
|
"@ungap/with-resolvers": "../../node_modules/@ungap/with-resolvers/index.js",
|
||||||
|
"@ungap/structured-clone/json": "../../node_modules/@ungap/structured-clone/esm/json.js",
|
||||||
"@pyscript/core": "../../esm/index.js"
|
"@pyscript/core": "../../esm/index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user