@pyscript/core 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cjs/custom/pyscript/exceptions.js +86 -0
- package/cjs/custom/pyscript/fetch.js +65 -0
- package/cjs/custom/pyscript.js +197 -0
- package/cjs/custom.js +201 -0
- package/cjs/index.js +2 -2
- package/cjs/interpreter/_python.js +8 -0
- package/cjs/interpreter/micropython.js +10 -12
- package/cjs/interpreter/pyodide.js +9 -7
- package/cjs/plugins/pyscript/exceptions.js +86 -0
- package/cjs/plugins/pyscript/fetch.js +65 -0
- package/cjs/plugins/pyscript.js +153 -83
- package/cjs/worker/class.js +4 -4
- package/cjs/worker/xworker.js +1 -1
- package/core.js +2 -0
- package/esm/custom/pyscript/exceptions.js +80 -0
- package/esm/custom/pyscript/fetch.js +63 -0
- package/esm/custom/pyscript.js +195 -0
- package/esm/{custom-types.js → custom.js} +70 -81
- package/esm/index.js +2 -2
- package/esm/interpreter/_python.js +6 -0
- package/esm/interpreter/micropython.js +10 -12
- package/esm/interpreter/pyodide.js +9 -7
- package/esm/script-handler.js +1 -1
- package/esm/worker/class.js +4 -4
- package/esm/worker/xworker.js +1 -1
- package/package.json +13 -11
- package/pyscript.js +2 -0
- package/types/coincident/window.d.ts +10 -0
- package/types/pyscript/pyscript.core/esm/custom.d.ts +54 -0
- package/types/pyscript/pyscript.core/esm/index.d.ts +1 -1
- package/types/pyscript/pyscript.core/esm/interpreter/_python.d.ts +2 -0
- package/types/pyscript/pyscript.core/esm/interpreter/micropython.d.ts +4 -2
- package/types/pyscript/pyscript.core/esm/interpreter/pyodide.d.ts +4 -2
- package/esm/worker/hooks.js +0 -1
- package/min.js +0 -2
@@ -0,0 +1,80 @@
|
|
1
|
+
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>`;
|
2
|
+
|
3
|
+
/**
|
4
|
+
* These error codes are used to identify the type of error that occurred.
|
5
|
+
* @see https://docs.pyscript.net/latest/reference/exceptions.html?highlight=errors
|
6
|
+
*/
|
7
|
+
export const ErrorCode = {
|
8
|
+
GENERIC: "PY0000", // Use this only for development then change to a more specific error code
|
9
|
+
FETCH_ERROR: "PY0001",
|
10
|
+
FETCH_NAME_ERROR: "PY0002",
|
11
|
+
// Currently these are created depending on error code received from fetching
|
12
|
+
FETCH_UNAUTHORIZED_ERROR: "PY0401",
|
13
|
+
FETCH_FORBIDDEN_ERROR: "PY0403",
|
14
|
+
FETCH_NOT_FOUND_ERROR: "PY0404",
|
15
|
+
FETCH_SERVER_ERROR: "PY0500",
|
16
|
+
FETCH_UNAVAILABLE_ERROR: "PY0503",
|
17
|
+
BAD_CONFIG: "PY1000",
|
18
|
+
MICROPIP_INSTALL_ERROR: "PY1001",
|
19
|
+
BAD_PLUGIN_FILE_EXTENSION: "PY2000",
|
20
|
+
NO_DEFAULT_EXPORT: "PY2001",
|
21
|
+
TOP_LEVEL_AWAIT: "PY9000",
|
22
|
+
};
|
23
|
+
|
24
|
+
export class UserError extends Error {
|
25
|
+
constructor(errorCode, message = "", messageType = "text") {
|
26
|
+
super(`(${errorCode}): ${message}`);
|
27
|
+
this.errorCode = errorCode;
|
28
|
+
this.messageType = messageType;
|
29
|
+
this.name = "UserError";
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
export class FetchError extends UserError {
|
34
|
+
constructor(errorCode, message) {
|
35
|
+
super(errorCode, message);
|
36
|
+
this.name = "FetchError";
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
export class InstallError extends UserError {
|
41
|
+
constructor(errorCode, message) {
|
42
|
+
super(errorCode, message);
|
43
|
+
this.name = "InstallError";
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
export function _createAlertBanner(
|
48
|
+
message,
|
49
|
+
level,
|
50
|
+
messageType = "text",
|
51
|
+
logMessage = true,
|
52
|
+
) {
|
53
|
+
switch (`log-${level}-${logMessage}`) {
|
54
|
+
case "log-error-true":
|
55
|
+
console.error(message);
|
56
|
+
break;
|
57
|
+
case "log-warning-true":
|
58
|
+
console.warn(message);
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
|
62
|
+
const content = messageType === "html" ? "innerHTML" : "textContent";
|
63
|
+
const banner = Object.assign(document.createElement("div"), {
|
64
|
+
className: `alert-banner py-${level}`,
|
65
|
+
[content]: message,
|
66
|
+
});
|
67
|
+
|
68
|
+
if (level === "warning") {
|
69
|
+
const closeButton = Object.assign(document.createElement("button"), {
|
70
|
+
id: "alert-close-button",
|
71
|
+
innerHTML: CLOSEBUTTON,
|
72
|
+
});
|
73
|
+
|
74
|
+
banner.appendChild(closeButton).addEventListener("click", () => {
|
75
|
+
banner.remove();
|
76
|
+
});
|
77
|
+
}
|
78
|
+
|
79
|
+
document.body.prepend(banner);
|
80
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { FetchError, ErrorCode } from "./exceptions";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* This is a fetch wrapper that handles any non 200 responses and throws a
|
5
|
+
* FetchError with the right ErrorCode. This is useful because our FetchError
|
6
|
+
* will automatically create an alert banner.
|
7
|
+
*
|
8
|
+
* @param {string} url - URL to fetch
|
9
|
+
* @param {Request} [options] - options to pass to fetch
|
10
|
+
* @returns {Promise<Response>}
|
11
|
+
*/
|
12
|
+
export async function robustFetch(url, options) {
|
13
|
+
let response;
|
14
|
+
|
15
|
+
// Note: We need to wrap fetch into a try/catch block because fetch
|
16
|
+
// throws a TypeError if the URL is invalid such as http://blah.blah
|
17
|
+
try {
|
18
|
+
response = await fetch(url, options);
|
19
|
+
} catch (err) {
|
20
|
+
const error = err;
|
21
|
+
let errMsg;
|
22
|
+
if (url.startsWith("http")) {
|
23
|
+
errMsg =
|
24
|
+
`Fetching from URL ${url} failed with error ` +
|
25
|
+
`'${error.message}'. Are your filename and path correct?`;
|
26
|
+
} else {
|
27
|
+
errMsg = `PyScript: Access to local files
|
28
|
+
(using [[fetch]] configurations in <py-config>)
|
29
|
+
is not available when directly opening a HTML file;
|
30
|
+
you must use a webserver to serve the additional files.
|
31
|
+
See <a style="text-decoration: underline;" href="https://github.com/pyscript/pyscript/issues/257#issuecomment-1119595062">this reference</a>
|
32
|
+
on starting a simple webserver with Python.
|
33
|
+
`;
|
34
|
+
}
|
35
|
+
throw new FetchError(ErrorCode.FETCH_ERROR, errMsg);
|
36
|
+
}
|
37
|
+
|
38
|
+
// Note that response.ok is true for 200-299 responses
|
39
|
+
if (!response.ok) {
|
40
|
+
const errorMsg = `Fetching from URL ${url} failed with error ${response.status} (${response.statusText}). Are your filename and path correct?`;
|
41
|
+
switch (response.status) {
|
42
|
+
case 404:
|
43
|
+
throw new FetchError(ErrorCode.FETCH_NOT_FOUND_ERROR, errorMsg);
|
44
|
+
case 401:
|
45
|
+
throw new FetchError(
|
46
|
+
ErrorCode.FETCH_UNAUTHORIZED_ERROR,
|
47
|
+
errorMsg,
|
48
|
+
);
|
49
|
+
case 403:
|
50
|
+
throw new FetchError(ErrorCode.FETCH_FORBIDDEN_ERROR, errorMsg);
|
51
|
+
case 500:
|
52
|
+
throw new FetchError(ErrorCode.FETCH_SERVER_ERROR, errorMsg);
|
53
|
+
case 503:
|
54
|
+
throw new FetchError(
|
55
|
+
ErrorCode.FETCH_UNAVAILABLE_ERROR,
|
56
|
+
errorMsg,
|
57
|
+
);
|
58
|
+
default:
|
59
|
+
throw new FetchError(ErrorCode.FETCH_ERROR, errorMsg);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
return response;
|
63
|
+
}
|
@@ -0,0 +1,195 @@
|
|
1
|
+
import "@ungap/with-resolvers";
|
2
|
+
import { $ } from "basic-devtools";
|
3
|
+
|
4
|
+
import { define } from "../index.js";
|
5
|
+
import { queryTarget } from "../script-handler.js";
|
6
|
+
import { defineProperty } from "../utils.js";
|
7
|
+
import { getText } from "../fetch-utils.js";
|
8
|
+
|
9
|
+
// TODO: should this utility be in core instead?
|
10
|
+
import { robustFetch as fetch } from "./pyscript/fetch.js";
|
11
|
+
|
12
|
+
// append ASAP CSS to avoid showing content
|
13
|
+
document.head.appendChild(document.createElement("style")).textContent = `
|
14
|
+
py-script, py-config {
|
15
|
+
display: none;
|
16
|
+
}
|
17
|
+
`;
|
18
|
+
|
19
|
+
(async () => {
|
20
|
+
// create a unique identifier when/if needed
|
21
|
+
let id = 0;
|
22
|
+
const getID = (prefix = "py") => `${prefix}-${id++}`;
|
23
|
+
|
24
|
+
// find the shared config for all py-script elements
|
25
|
+
let config;
|
26
|
+
let pyConfig = $("py-config");
|
27
|
+
if (pyConfig) config = pyConfig.getAttribute("src") || pyConfig.textContent;
|
28
|
+
else {
|
29
|
+
pyConfig = $('script[type="py"]');
|
30
|
+
config = pyConfig?.getAttribute("config");
|
31
|
+
}
|
32
|
+
|
33
|
+
if (/^https?:\/\//.test(config)) config = await fetch(config).then(getText);
|
34
|
+
|
35
|
+
// generic helper to disambiguate between custom element and script
|
36
|
+
const isScript = (element) => element.tagName === "SCRIPT";
|
37
|
+
|
38
|
+
// helper for all script[type="py"] out there
|
39
|
+
const before = (script) => {
|
40
|
+
defineProperty(document, "currentScript", {
|
41
|
+
configurable: true,
|
42
|
+
get: () => script,
|
43
|
+
});
|
44
|
+
};
|
45
|
+
|
46
|
+
const after = () => {
|
47
|
+
delete document.currentScript;
|
48
|
+
};
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Given a generic DOM Element, tries to fetch the 'src' attribute, if present.
|
52
|
+
* It either throws an error if the 'src' can't be fetched or it returns a fallback
|
53
|
+
* content as source.
|
54
|
+
*/
|
55
|
+
const fetchSource = async (tag) => {
|
56
|
+
if (tag.hasAttribute("src")) {
|
57
|
+
try {
|
58
|
+
const response = await fetch(tag.getAttribute("src"));
|
59
|
+
return response.then(getText);
|
60
|
+
} catch (error) {
|
61
|
+
// TODO _createAlertBanner(err) instead ?
|
62
|
+
alert(error.message);
|
63
|
+
throw error;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
return tag.textContent;
|
67
|
+
};
|
68
|
+
|
69
|
+
// common life-cycle handlers for any node
|
70
|
+
const bootstrapNodeAndPlugins = (pyodide, element, callback, hook) => {
|
71
|
+
if (isScript(element)) callback(element);
|
72
|
+
for (const fn of hooks[hook]) fn(pyodide, element);
|
73
|
+
};
|
74
|
+
|
75
|
+
const addDisplay = (element) => {
|
76
|
+
const id = isScript(element) ? element.target.id : element.id;
|
77
|
+
return `
|
78
|
+
# this code is just for demo purpose but the basics work
|
79
|
+
def _display(what, target="${id}", append=True):
|
80
|
+
from js import document
|
81
|
+
element = document.getElementById(target)
|
82
|
+
element.textContent = what
|
83
|
+
display = _display
|
84
|
+
`;
|
85
|
+
};
|
86
|
+
|
87
|
+
// define the module as both `<script type="py">` and `<py-script>`
|
88
|
+
define("py", {
|
89
|
+
config,
|
90
|
+
env: "py-script",
|
91
|
+
interpreter: "pyodide",
|
92
|
+
codeBeforeRunWorker() {
|
93
|
+
const { codeBeforeRunWorker: set } = hooks;
|
94
|
+
const prefix = 'print("codeBeforeRunWorker")';
|
95
|
+
return [prefix].concat(...set).join("\n");
|
96
|
+
},
|
97
|
+
codeAfterRunWorker() {
|
98
|
+
const { codeAfterRunWorker: set } = hooks;
|
99
|
+
const prefix = 'print("codeAfterRunWorker")';
|
100
|
+
return [prefix].concat(...set).join("\n");
|
101
|
+
},
|
102
|
+
onBeforeRun(pyodide, element) {
|
103
|
+
bootstrapNodeAndPlugins(pyodide, element, before, "onBeforeRun");
|
104
|
+
pyodide.interpreter.runPython(addDisplay(element));
|
105
|
+
},
|
106
|
+
onBeforeRunAync(pyodide, element) {
|
107
|
+
pyodide.interpreter.runPython(addDisplay(element));
|
108
|
+
bootstrapNodeAndPlugins(
|
109
|
+
pyodide,
|
110
|
+
element,
|
111
|
+
before,
|
112
|
+
"onBeforeRunAync",
|
113
|
+
);
|
114
|
+
},
|
115
|
+
onAfterRun(pyodide, element) {
|
116
|
+
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRun");
|
117
|
+
},
|
118
|
+
onAfterRunAsync(pyodide, element) {
|
119
|
+
bootstrapNodeAndPlugins(pyodide, element, after, "onAfterRunAsync");
|
120
|
+
},
|
121
|
+
async onRuntimeReady(pyodide, element) {
|
122
|
+
// allows plugins to do whatever they want with the element
|
123
|
+
// before regular stuff happens in here
|
124
|
+
for (const callback of hooks.onRuntimeReady)
|
125
|
+
callback(pyodide, element);
|
126
|
+
if (isScript(element)) {
|
127
|
+
const {
|
128
|
+
attributes: { async: isAsync, target },
|
129
|
+
} = element;
|
130
|
+
const hasTarget = !!target?.value;
|
131
|
+
const show = hasTarget
|
132
|
+
? queryTarget(target.value)
|
133
|
+
: document.createElement("script-py");
|
134
|
+
|
135
|
+
if (!hasTarget) element.after(show);
|
136
|
+
if (!show.id) show.id = getID();
|
137
|
+
|
138
|
+
// allows the code to retrieve the target element via
|
139
|
+
// document.currentScript.target if needed
|
140
|
+
defineProperty(element, "target", { value: show });
|
141
|
+
|
142
|
+
pyodide[`run${isAsync ? "Async" : ""}`](
|
143
|
+
await fetchSource(element),
|
144
|
+
);
|
145
|
+
} else {
|
146
|
+
// resolve PyScriptElement to allow connectedCallback
|
147
|
+
element._pyodide.resolve(pyodide);
|
148
|
+
}
|
149
|
+
},
|
150
|
+
});
|
151
|
+
|
152
|
+
class PyScriptElement extends HTMLElement {
|
153
|
+
constructor() {
|
154
|
+
if (!super().id) this.id = getID();
|
155
|
+
this._pyodide = Promise.withResolvers();
|
156
|
+
this.srcCode = "";
|
157
|
+
this.executed = false;
|
158
|
+
}
|
159
|
+
async connectedCallback() {
|
160
|
+
if (!this.executed) {
|
161
|
+
this.executed = true;
|
162
|
+
const { run } = await this._pyodide.promise;
|
163
|
+
this.srcCode = await fetchSource(this);
|
164
|
+
this.textContent = "";
|
165
|
+
const result = run(this.srcCode);
|
166
|
+
if (!this.textContent && result) this.textContent = result;
|
167
|
+
this.style.display = "block";
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
customElements.define("py-script", PyScriptElement);
|
173
|
+
})();
|
174
|
+
|
175
|
+
export const hooks = {
|
176
|
+
/** @type {Set<function>} */
|
177
|
+
onBeforeRun: new Set(),
|
178
|
+
/** @type {Set<function>} */
|
179
|
+
onBeforeRunAync: new Set(),
|
180
|
+
/** @type {Set<function>} */
|
181
|
+
onAfterRun: new Set(),
|
182
|
+
/** @type {Set<function>} */
|
183
|
+
onAfterRunAsync: new Set(),
|
184
|
+
/** @type {Set<function>} */
|
185
|
+
onRuntimeReady: new Set(),
|
186
|
+
|
187
|
+
/** @type {Set<string>} */
|
188
|
+
codeBeforeRunWorker: new Set(),
|
189
|
+
/** @type {Set<string>} */
|
190
|
+
codeBeforeRunWorkerAsync: new Set(),
|
191
|
+
/** @type {Set<string>} */
|
192
|
+
codeAfterRunWorker: new Set(),
|
193
|
+
/** @type {Set<string>} */
|
194
|
+
codeAfterRunWorkerAsync: new Set(),
|
195
|
+
};
|
@@ -12,8 +12,6 @@ import { getRuntimeID } from "./loader.js";
|
|
12
12
|
import { io } from "./interpreter/_utils.js";
|
13
13
|
import { addAllListeners } from "./listeners.js";
|
14
14
|
|
15
|
-
import workerHooks from "./worker/hooks.js";
|
16
|
-
|
17
15
|
export const CUSTOM_SELECTORS = [];
|
18
16
|
|
19
17
|
/**
|
@@ -26,7 +24,6 @@ export const CUSTOM_SELECTORS = [];
|
|
26
24
|
* @prop {(path:string, data:ArrayBuffer) => void} writeFile an utility to write a file in the virtual FS, if available
|
27
25
|
*/
|
28
26
|
|
29
|
-
const patched = new Map();
|
30
27
|
const types = new Map();
|
31
28
|
const waitList = new Map();
|
32
29
|
|
@@ -52,7 +49,7 @@ export const handleCustomType = (node) => {
|
|
52
49
|
} = options;
|
53
50
|
const name = getRuntimeID(runtime, version);
|
54
51
|
const id = env || `${name}${config ? `|${config}` : ""}`;
|
55
|
-
const { interpreter: engine, XWorker } = getDetails(
|
52
|
+
const { interpreter: engine, XWorker: Worker } = getDetails(
|
56
53
|
runtime,
|
57
54
|
id,
|
58
55
|
name,
|
@@ -60,87 +57,79 @@ export const handleCustomType = (node) => {
|
|
60
57
|
config,
|
61
58
|
);
|
62
59
|
engine.then((interpreter) => {
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
const method = module[name];
|
102
|
-
module[name] = async function (interpreter, code) {
|
103
|
-
if (before)
|
104
|
-
await before.call(this, resolved, node);
|
105
|
-
const result = await method.call(
|
106
|
-
this,
|
107
|
-
interpreter,
|
108
|
-
code,
|
109
|
-
);
|
110
|
-
if (after)
|
111
|
-
await after.call(this, resolved, node);
|
112
|
-
return result;
|
113
|
-
};
|
114
|
-
}
|
115
|
-
|
116
|
-
// setup XWorker hooks, allowing strings to be forwarded to the worker
|
117
|
-
// whenever it's created, as functions can't possibly be serialized
|
118
|
-
// unless these are pure with no outer scope access (or globals vars)
|
119
|
-
// so that making it strings disambiguate about their running context.
|
120
|
-
workerHooks.set(XWorker, {
|
121
|
-
beforeRun: codeBeforeRunWorker,
|
122
|
-
beforeRunAsync: codeBeforeRunWorkerAsync,
|
123
|
-
afterRun: codeAfterRunWorker,
|
124
|
-
afterRunAsync: codeAfterRunWorkerAsync,
|
125
|
-
});
|
126
|
-
|
127
|
-
module.setGlobal(interpreter, "XWorker", XWorker);
|
128
|
-
|
129
|
-
const resolved = {
|
130
|
-
type,
|
131
|
-
interpreter,
|
132
|
-
XWorker,
|
133
|
-
io: io.get(interpreter),
|
134
|
-
config: structuredClone(configs.get(name)),
|
135
|
-
run: module.run.bind(module, interpreter),
|
136
|
-
runAsync: module.runAsync.bind(module, interpreter),
|
60
|
+
const module = create(defaultRegistry.get(runtime));
|
61
|
+
|
62
|
+
const {
|
63
|
+
onBeforeRun,
|
64
|
+
onBeforeRunAsync,
|
65
|
+
onAfterRun,
|
66
|
+
onAfterRunAsync,
|
67
|
+
codeBeforeRunWorker,
|
68
|
+
codeBeforeRunWorkerAsync,
|
69
|
+
codeAfterRunWorker,
|
70
|
+
codeAfterRunWorkerAsync,
|
71
|
+
} = options;
|
72
|
+
|
73
|
+
const hooks = {
|
74
|
+
beforeRun: codeBeforeRunWorker?.(),
|
75
|
+
beforeRunAsync: codeBeforeRunWorkerAsync?.(),
|
76
|
+
afterRun: codeAfterRunWorker?.(),
|
77
|
+
afterRunAsync: codeAfterRunWorkerAsync?.(),
|
78
|
+
};
|
79
|
+
|
80
|
+
const XWorker = function XWorker(...args) {
|
81
|
+
return Worker.apply(hooks, args);
|
82
|
+
};
|
83
|
+
|
84
|
+
// These two loops mimic a `new Map(arrayContent)` without needing
|
85
|
+
// the new Map overhead so that [name, [before, after]] can be easily destructured
|
86
|
+
// and new sync or async patches become easy to add (when the logic is the same).
|
87
|
+
|
88
|
+
// patch sync
|
89
|
+
for (const [name, [before, after]] of [
|
90
|
+
["run", [onBeforeRun, onAfterRun]],
|
91
|
+
]) {
|
92
|
+
const method = module[name];
|
93
|
+
module[name] = function (interpreter, code) {
|
94
|
+
if (before) before.call(this, resolved, node);
|
95
|
+
const result = method.call(this, interpreter, code);
|
96
|
+
if (after) after.call(this, resolved, node);
|
97
|
+
return result;
|
137
98
|
};
|
99
|
+
}
|
138
100
|
|
139
|
-
|
140
|
-
|
101
|
+
// patch async
|
102
|
+
for (const [name, [before, after]] of [
|
103
|
+
["runAsync", [onBeforeRunAsync, onAfterRunAsync]],
|
104
|
+
]) {
|
105
|
+
const method = module[name];
|
106
|
+
module[name] = async function (interpreter, code) {
|
107
|
+
if (before) await before.call(this, resolved, node);
|
108
|
+
const result = await method.call(
|
109
|
+
this,
|
110
|
+
interpreter,
|
111
|
+
code,
|
112
|
+
);
|
113
|
+
if (after) await after.call(this, resolved, node);
|
114
|
+
return result;
|
115
|
+
};
|
141
116
|
}
|
142
117
|
|
143
|
-
|
118
|
+
module.setGlobal(interpreter, "XWorker", XWorker);
|
119
|
+
|
120
|
+
const resolved = {
|
121
|
+
type,
|
122
|
+
interpreter,
|
123
|
+
XWorker,
|
124
|
+
io: io.get(interpreter),
|
125
|
+
config: structuredClone(configs.get(name)),
|
126
|
+
run: module.run.bind(module, interpreter),
|
127
|
+
runAsync: module.runAsync.bind(module, interpreter),
|
128
|
+
};
|
129
|
+
|
130
|
+
resolve(resolved);
|
131
|
+
|
132
|
+
onRuntimeReady?.(resolved, node);
|
144
133
|
});
|
145
134
|
}
|
146
135
|
}
|
package/esm/index.js
CHANGED
@@ -4,10 +4,10 @@ import xworker from "./worker/class.js";
|
|
4
4
|
import { handle } from "./script-handler.js";
|
5
5
|
import { assign } from "./utils.js";
|
6
6
|
import { selectors, prefixes } from "./interpreters.js";
|
7
|
-
import { CUSTOM_SELECTORS, handleCustomType } from "./custom
|
7
|
+
import { CUSTOM_SELECTORS, handleCustomType } from "./custom.js";
|
8
8
|
import { listener, addAllListeners } from "./listeners.js";
|
9
9
|
|
10
|
-
export { define, whenDefined } from "./custom
|
10
|
+
export { define, whenDefined } from "./custom.js";
|
11
11
|
export const XWorker = xworker();
|
12
12
|
|
13
13
|
const INTERPRETER_SELECTORS = selectors.join(",");
|
@@ -7,6 +7,12 @@ export const run = (interpreter, code) => interpreter.runPython(clean(code));
|
|
7
7
|
export const runAsync = (interpreter, code) =>
|
8
8
|
interpreter.runPythonAsync(clean(code));
|
9
9
|
|
10
|
+
export const setGlobal = (interpreter, name, value) =>
|
11
|
+
interpreter.globals.set(name, value);
|
12
|
+
|
13
|
+
export const deleteGlobal = (interpreter, name) =>
|
14
|
+
interpreter.globals.delete(name);
|
15
|
+
|
10
16
|
export const writeFile = ({ FS }, path, buffer) =>
|
11
17
|
writeFileUtil(FS, path, buffer);
|
12
18
|
/* c8 ignore stop */
|
@@ -1,5 +1,11 @@
|
|
1
1
|
import { fetchPaths, stdio } from "./_utils.js";
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
run,
|
4
|
+
runAsync,
|
5
|
+
setGlobal,
|
6
|
+
deleteGlobal,
|
7
|
+
writeFile,
|
8
|
+
} from "./_python.js";
|
3
9
|
|
4
10
|
const type = "micropython";
|
5
11
|
|
@@ -7,7 +13,7 @@ const type = "micropython";
|
|
7
13
|
/* c8 ignore start */
|
8
14
|
export default {
|
9
15
|
type,
|
10
|
-
module: (version = "1.20.0-
|
16
|
+
module: (version = "1.20.0-253") =>
|
11
17
|
`https://cdn.jsdelivr.net/npm/@micropython/micropython-webassembly-pyscript@${version}/micropython.mjs`,
|
12
18
|
async engine({ loadMicroPython }, config, url) {
|
13
19
|
const { stderr, stdout, get } = stdio();
|
@@ -16,16 +22,8 @@ export default {
|
|
16
22
|
if (config.fetch) await fetchPaths(this, runtime, config.fetch);
|
17
23
|
return runtime;
|
18
24
|
},
|
19
|
-
setGlobal
|
20
|
-
|
21
|
-
globalThis[id] = value;
|
22
|
-
this.run(interpreter, `from js import ${id};${name}=${id};`);
|
23
|
-
},
|
24
|
-
deleteGlobal(interpreter, name) {
|
25
|
-
const id = `__pyscript_${this.type}_${name}`;
|
26
|
-
this.run(interpreter, `del ${id};del ${name}`);
|
27
|
-
delete globalThis[id];
|
28
|
-
},
|
25
|
+
setGlobal,
|
26
|
+
deleteGlobal,
|
29
27
|
run,
|
30
28
|
runAsync,
|
31
29
|
writeFile,
|
@@ -1,5 +1,11 @@
|
|
1
1
|
import { fetchPaths, stdio } from "./_utils.js";
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
run,
|
4
|
+
runAsync,
|
5
|
+
setGlobal,
|
6
|
+
deleteGlobal,
|
7
|
+
writeFile,
|
8
|
+
} from "./_python.js";
|
3
9
|
|
4
10
|
const type = "pyodide";
|
5
11
|
|
@@ -24,12 +30,8 @@ export default {
|
|
24
30
|
}
|
25
31
|
return interpreter;
|
26
32
|
},
|
27
|
-
setGlobal
|
28
|
-
|
29
|
-
},
|
30
|
-
deleteGlobal(interpreter, name) {
|
31
|
-
interpreter.globals.delete(name);
|
32
|
-
},
|
33
|
+
setGlobal,
|
34
|
+
deleteGlobal,
|
33
35
|
run,
|
34
36
|
runAsync,
|
35
37
|
writeFile,
|
package/esm/script-handler.js
CHANGED
@@ -12,7 +12,7 @@ const getRoot = (script) => {
|
|
12
12
|
return parent;
|
13
13
|
};
|
14
14
|
|
15
|
-
const queryTarget = (script, idOrSelector) => {
|
15
|
+
export const queryTarget = (script, idOrSelector) => {
|
16
16
|
const root = getRoot(script);
|
17
17
|
return root.getElementById(idOrSelector) || $(idOrSelector, root);
|
18
18
|
};
|
package/esm/worker/class.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
import
|
1
|
+
import * as JSON from "@ungap/structured-clone/json";
|
2
|
+
import coincident from "coincident/window";
|
2
3
|
import xworker from "./xworker.js";
|
3
4
|
import { assign, defineProperties, absoluteURL } from "../utils.js";
|
4
5
|
import { getText } from "../fetch-utils.js";
|
5
|
-
import workerHooks from "./hooks.js";
|
6
6
|
|
7
7
|
/**
|
8
8
|
* @typedef {Object} WorkerOptions custom configuration
|
@@ -19,9 +19,9 @@ export default (...args) =>
|
|
19
19
|
* @returns {Worker}
|
20
20
|
*/
|
21
21
|
function XWorker(url, options) {
|
22
|
-
const hooks = workerHooks.get(XWorker);
|
23
22
|
const worker = xworker();
|
24
23
|
const { postMessage } = worker;
|
24
|
+
const hooks = this instanceof XWorker ? void 0 : this;
|
25
25
|
if (args.length) {
|
26
26
|
const [type, version] = args;
|
27
27
|
options = assign({}, options || { type, version });
|
@@ -39,7 +39,7 @@ export default (...args) =>
|
|
39
39
|
),
|
40
40
|
},
|
41
41
|
sync: {
|
42
|
-
value: coincident(worker),
|
42
|
+
value: coincident(worker, JSON).proxy,
|
43
43
|
},
|
44
44
|
});
|
45
45
|
};
|