@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.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/cjs/custom/pyscript/exceptions.js +86 -0
  3. package/cjs/custom/pyscript/fetch.js +65 -0
  4. package/cjs/custom/pyscript.js +197 -0
  5. package/cjs/custom.js +201 -0
  6. package/cjs/index.js +2 -2
  7. package/cjs/interpreter/_python.js +8 -0
  8. package/cjs/interpreter/micropython.js +10 -12
  9. package/cjs/interpreter/pyodide.js +9 -7
  10. package/cjs/plugins/pyscript/exceptions.js +86 -0
  11. package/cjs/plugins/pyscript/fetch.js +65 -0
  12. package/cjs/plugins/pyscript.js +153 -83
  13. package/cjs/worker/class.js +4 -4
  14. package/cjs/worker/xworker.js +1 -1
  15. package/core.js +2 -0
  16. package/esm/custom/pyscript/exceptions.js +80 -0
  17. package/esm/custom/pyscript/fetch.js +63 -0
  18. package/esm/custom/pyscript.js +195 -0
  19. package/esm/{custom-types.js → custom.js} +70 -81
  20. package/esm/index.js +2 -2
  21. package/esm/interpreter/_python.js +6 -0
  22. package/esm/interpreter/micropython.js +10 -12
  23. package/esm/interpreter/pyodide.js +9 -7
  24. package/esm/script-handler.js +1 -1
  25. package/esm/worker/class.js +4 -4
  26. package/esm/worker/xworker.js +1 -1
  27. package/package.json +13 -11
  28. package/pyscript.js +2 -0
  29. package/types/coincident/window.d.ts +10 -0
  30. package/types/pyscript/pyscript.core/esm/custom.d.ts +54 -0
  31. package/types/pyscript/pyscript.core/esm/index.d.ts +1 -1
  32. package/types/pyscript/pyscript.core/esm/interpreter/_python.d.ts +2 -0
  33. package/types/pyscript/pyscript.core/esm/interpreter/micropython.d.ts +4 -2
  34. package/types/pyscript/pyscript.core/esm/interpreter/pyodide.d.ts +4 -2
  35. package/esm/worker/hooks.js +0 -1
  36. 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 &lt;py-config&gt;)
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
- if (!patched.has(id)) {
64
- const module = create(defaultRegistry.get(runtime));
65
- const {
66
- onBeforeRun,
67
- onBeforeRunAsync,
68
- onAfterRun,
69
- onAfterRunAsync,
70
- codeBeforeRunWorker,
71
- codeBeforeRunWorkerAsync,
72
- codeAfterRunWorker,
73
- codeAfterRunWorkerAsync,
74
- } = options;
75
-
76
- // These two loops mimic a `new Map(arrayContent)` without needing
77
- // the new Map overhead so that [name, [before, after]] can be easily destructured
78
- // and new sync or async patches become easy to add (when the logic is the same).
79
-
80
- // patch sync
81
- for (const [name, [before, after]] of [
82
- ["run", [onBeforeRun, onAfterRun]],
83
- ]) {
84
- const method = module[name];
85
- module[name] = function (interpreter, code) {
86
- if (before) before.call(this, resolved, node);
87
- const result = method.call(
88
- this,
89
- interpreter,
90
- code,
91
- );
92
- if (after) after.call(this, resolved, node);
93
- return result;
94
- };
95
- }
96
-
97
- // patch async
98
- for (const [name, [before, after]] of [
99
- ["runAsync", [onBeforeRunAsync, onAfterRunAsync]],
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
- patched.set(id, resolved);
140
- resolve(resolved);
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
- onRuntimeReady?.(patched.get(id), node);
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-types.js";
7
+ import { CUSTOM_SELECTORS, handleCustomType } from "./custom.js";
8
8
  import { listener, addAllListeners } from "./listeners.js";
9
9
 
10
- export { define, whenDefined } from "./custom-types.js";
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 { run, runAsync, writeFile } from "./_python.js";
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-239") =>
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(interpreter, name, value) {
20
- const id = `__pyscript_${this.type}_${name}`;
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 { run, runAsync, writeFile } from "./_python.js";
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(interpreter, name, value) {
28
- interpreter.globals.set(name, value);
29
- },
30
- deleteGlobal(interpreter, name) {
31
- interpreter.globals.delete(name);
32
- },
33
+ setGlobal,
34
+ deleteGlobal,
33
35
  run,
34
36
  runAsync,
35
37
  writeFile,
@@ -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
  };
@@ -1,8 +1,8 @@
1
- import coincident from "coincident/structured";
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
  };