diff --git a/docs/project/changelog.md b/docs/project/changelog.md index dd051b53b..1f46e521e 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -55,6 +55,11 @@ substitutions: way. {pr}`2178` +- {{Enhancement}} When Pyodide is loaded as an ES6 module, no global + `loadPyodide` variable is created (instead, it should be accessed as an + attribute on the module). + {pr}`2249` + - {{Breaking}} Removed the `skip-host` key from the `meta.yaml` format. If needed, install a host copy of the package with pip instead. {pr}`2256` diff --git a/src/core/error_handling.ts b/src/core/error_handling.ts index 194ec6218..1d518b5e7 100644 --- a/src/core/error_handling.ts +++ b/src/core/error_handling.ts @@ -254,10 +254,6 @@ Module.handle_js_error = function (e: any) { * easiest way is to only handle the exception in Python. */ export class PythonError extends Error { - /** - * The Python traceback. - */ - message: string; /** The address of the error we are wrapping. We may later compare this * against sys.last_value. * WARNING: we don't own a reference to this pointer, dereferencing it diff --git a/src/core/pyproxy.ts b/src/core/pyproxy.ts index e01f7f6c2..e1bf3a275 100644 --- a/src/core/pyproxy.ts +++ b/src/core/pyproxy.ts @@ -52,18 +52,23 @@ export function isPyProxy(jsobj: any): jsobj is PyProxy { } API.isPyProxy = isPyProxy; +declare var FinalizationRegistry: any; +declare var globalThis: any; + if (globalThis.FinalizationRegistry) { - Module.finalizationRegistry = new FinalizationRegistry(([ptr, cache]) => { - cache.leaked = true; - pyproxy_decref_cache(cache); - try { - Module._Py_DecRef(ptr); - } catch (e) { - // I'm not really sure what happens if an error occurs inside of a - // finalizer... - API.fatal_error(e); + Module.finalizationRegistry = new FinalizationRegistry( + ([ptr, cache]: [ptr: number, cache: PyProxyCache]) => { + cache.leaked = true; + pyproxy_decref_cache(cache); + try { + Module._Py_DecRef(ptr); + } catch (e) { + // I'm not really sure what happens if an error occurs inside of a + // finalizer... + API.fatal_error(e); + } } - }); + ); // For some unclear reason this code screws up selenium FirefoxDriver. Works // fine in chrome and when I test it in browser. It seems to be sensitive to // changes that don't make a difference to the semantics. @@ -155,8 +160,8 @@ Module.pyproxy_new = function (ptrobj: number, cache?: PyProxyCache) { }; function _getPtr(jsobj: any) { - let ptr = jsobj.$$.ptr; - if (ptr === null) { + let ptr: number = jsobj.$$.ptr; + if (ptr === 0) { throw new Error(jsobj.$$.destroyed_msg); } return ptr; @@ -236,7 +241,7 @@ function pyproxy_decref_cache(cache: PyProxyCache) { } Module.pyproxy_destroy = function (proxy: PyProxy, destroyed_msg: string) { - if (proxy.$$.ptr === null) { + if (proxy.$$.ptr === 0) { return; } let ptrobj = _getPtr(proxy); @@ -254,7 +259,7 @@ Module.pyproxy_destroy = function (proxy: PyProxy, destroyed_msg: string) { // Maybe the destructor will call JavaScript code that will somehow try // to use this proxy. Mark it deleted before decrementing reference count // just in case! - proxy.$$.ptr = null; + proxy.$$.ptr = 0; destroyed_msg += "\n" + `The object was of type "${proxy_type}" and `; if (proxy_repr) { destroyed_msg += `had repr "${proxy_repr}"`; @@ -941,7 +946,7 @@ let PyProxyHandlers = { python_setattr(jsobj, jskey, jsval); return true; }, - deleteProperty(jsobj: PyProxyClass, jskey: any) { + deleteProperty(jsobj: PyProxyClass, jskey: any): boolean { let descr = Object.getOwnPropertyDescriptor(jsobj, jskey); if (descr && !descr.writable) { throw new TypeError(`Cannot delete read only field '${jskey}'`); @@ -955,7 +960,7 @@ let PyProxyHandlers = { python_delattr(jsobj, jskey); // Must return "false" if "jskey" is a nonconfigurable own property. // Otherwise JavaScript will throw a TypeError. - return !descr || descr.configurable; + return !descr || !!descr.configurable; }, ownKeys(jsobj: PyProxyClass) { let ptrobj = _getPtr(jsobj); @@ -1484,6 +1489,7 @@ export class PyBuffer { API.fatal_error(e); } this._released = true; + // @ts-ignore this.data = null; } } diff --git a/src/js/index.test-d.ts b/src/js/index.test-d.ts index b53bcf130..66e887a21 100644 --- a/src/js/index.test-d.ts +++ b/src/js/index.test-d.ts @@ -14,7 +14,7 @@ import { PyProxyCallable, PyBuffer, TypedArray, -} from "../../build/pyodide"; +} from "./pyodide"; async function main() { let pyodide = await loadPyodide({ indexURL: "blah" }); diff --git a/src/js/load-package.ts b/src/js/load-package.ts index a25dcf6aa..d3f9f037a 100644 --- a/src/js/load-package.ts +++ b/src/js/load-package.ts @@ -46,7 +46,7 @@ const DEFAULT_CHANNEL = "default channel"; // Regexp for validating package name and URI const package_uri_regexp = /^.*?([^\/]*)\.whl$/; -function _uri_to_package_name(package_uri: string): string { +function _uri_to_package_name(package_uri: string): string | undefined { let match = package_uri_regexp.exec(package_uri); if (match) { let wheel_name = match[1].toLowerCase(); @@ -202,6 +202,7 @@ function createLock() { let releaseLock: () => void; _lock = new Promise((resolve) => (releaseLock = resolve)); await old_lock; + // @ts-ignore return releaseLock; } return acquireLock; diff --git a/src/js/pyodide.ts b/src/js/pyodide.ts index b803f10ff..0c9e4f2ba 100644 --- a/src/js/pyodide.ts +++ b/src/js/pyodide.ts @@ -233,7 +233,7 @@ export async function loadPyodide(config: { ); setStandardStreams(config.stdin, config.stdout, config.stderr); - setHomeDirectory(config.homedir); + setHomeDirectory(config.homedir!); let moduleLoaded = new Promise((r) => (Module.postRun = r)); @@ -270,4 +270,3 @@ export async function loadPyodide(config: { pyodide.runPython("print('Python initialization complete')"); return pyodide; } -(globalThis as any).loadPyodide = loadPyodide; diff --git a/src/js/pyodide.umd.ts b/src/js/pyodide.umd.ts new file mode 100644 index 000000000..a7bfb8040 --- /dev/null +++ b/src/js/pyodide.umd.ts @@ -0,0 +1,3 @@ +import { loadPyodide } from "./pyodide"; +export { loadPyodide }; +(globalThis as any).loadPyodide = loadPyodide; diff --git a/src/js/rollup.config.js b/src/js/rollup.config.js index 36635721b..612f0629d 100644 --- a/src/js/rollup.config.js +++ b/src/js/rollup.config.js @@ -3,7 +3,7 @@ import { nodeResolve } from "@rollup/plugin-node-resolve"; import { terser } from "rollup-plugin-terser"; import ts from "rollup-plugin-ts"; -function config({ input, format, minify, ext = "js" }) { +function config({ input, format, minify, ext }) { const dir = `build/`; // const minifierSuffix = minify ? ".min" : ""; const minifierSuffix = ""; @@ -11,7 +11,7 @@ function config({ input, format, minify, ext = "js" }) { input: `./src/js/${input}.ts`, output: { name: "loadPyodide", - file: `${dir}/${input}${minifierSuffix}.${ext}`, + file: `${dir}/pyodide${minifierSuffix}.${ext}`, format, sourcemap: true, }, @@ -39,5 +39,5 @@ export default [ // { input: "pyodide", format: "esm", minify: false, ext: "mjs" }, { input: "pyodide", format: "esm", minify: true, ext: "mjs" }, // { input: "pyodide", format: "umd", minify: false }, - { input: "pyodide", format: "umd", minify: true }, + { input: "pyodide.umd", format: "umd", minify: true, ext: "js" }, ].map(config); diff --git a/src/js/tsconfig.json b/src/js/tsconfig.json index 16563b83b..47afc5a01 100644 --- a/src/js/tsconfig.json +++ b/src/js/tsconfig.json @@ -4,7 +4,6 @@ "declaration": true, "lib": ["ES2021", "DOM"], "outDir": "../../build", - "rootDir": ".", "esModuleInterop": true, "target": "ES6", "module": "ES2020", diff --git a/src/templates/module_test.html b/src/templates/module_test.html index 3f145e5ce..6b4ad3dbb 100644 --- a/src/templates/module_test.html +++ b/src/templates/module_test.html @@ -19,7 +19,7 @@ diff --git a/src/tests/test_typeconversions.py b/src/tests/test_typeconversions.py index b6d68e467..0811b5adb 100644 --- a/src/tests/test_typeconversions.py +++ b/src/tests/test_typeconversions.py @@ -322,7 +322,7 @@ def test_python2js_track_proxies(selenium): function check(l){ for(let x of l){ if(pyodide.isPyProxy(x)){ - assert(() => x.$$.ptr === null); + assert(() => x.$$.ptr === 0); } else { check(x); }