From 391c43e6620743ae70934d997266d03d510bde6f Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 30 Jan 2022 11:08:44 -0800 Subject: [PATCH] MAINT Separate some things out of Module (#2144) --- Makefile | 10 ++-- Makefile.envs | 3 +- conftest.py | 20 ++++---- packages/micropip/src/micropip/_micropip.py | 2 +- pyodide-build/pyodide_build/testing.py | 2 +- src/core/error_handling.c | 7 ++- src/core/error_handling.ts | 10 ++-- src/core/hiwire.c | 6 +-- src/core/js2python.js | 2 +- src/core/jsproxy.c | 2 +- src/core/keyboard_interrupt.c | 4 +- src/core/main.c | 2 +- src/core/pre.js | 1 + src/core/pyproxy.c | 2 +- src/core/pyproxy.ts | 50 +++++++++--------- src/js/api.ts | 50 +++++++++--------- src/js/load-package.ts | 37 +++++++------ src/js/module.ts | 3 ++ src/js/pyodide.ts | 57 +++++++++++---------- src/templates/console.html | 2 +- src/tests/test_package_loading.py | 2 +- src/tests/test_pyodide.py | 6 +-- src/tests/test_pyproxy.py | 6 +-- 23 files changed, 151 insertions(+), 135 deletions(-) create mode 100644 src/core/pre.js diff --git a/Makefile b/Makefile index 19814853f..9db2aeba5 100644 --- a/Makefile +++ b/Makefile @@ -152,9 +152,13 @@ lint: node_modules/.installed packages/micropip/src/ # Format checks - find src -type f -regex '.*\.\(c\|h\)' \ - | xargs clang-format-6.0 -output-replacements-xml \ - | (! grep ' /dev/null ; \ + if [ $$? -eq 0 ] ; then \ + echo clang-format errors for $$file ; \ + exit 1 ; \ + fi ; \ + done npx prettier --check . black --check . diff --git a/Makefile.envs b/Makefile.envs index 46881a433..75cf22329 100644 --- a/Makefile.envs +++ b/Makefile.envs @@ -87,7 +87,8 @@ export MAIN_MODULE_LDFLAGS= $(LDFLAGS_BASE) \ --exclude-file "*__pycache__*" \ --exclude-file "*/test/*" \ --exclude-file "*/tests/*" \ - --exclude-file "*/distutils/*" + --exclude-file "*/distutils/*" \ + --pre-js src/core/pre.js export SIDE_MODULE_CXXFLAGS = $(CXXFLAGS_BASE) diff --git a/conftest.py b/conftest.py index 057f3647a..70b879a33 100644 --- a/conftest.py +++ b/conftest.py @@ -152,7 +152,7 @@ class SeleniumWrapper: let pyodide = await loadPyodide({ indexURL : './', fullStdLib: false, jsglobals : self }); self.pyodide = pyodide; globalThis.pyodide = pyodide; - pyodide._module.inTestHoist = true; // improve some error messages for tests + pyodide._api.inTestHoist = true; // improve some error messages for tests """ ) @@ -170,11 +170,11 @@ class SeleniumWrapper: pyodide.pyodide_py.register_js_module; pyodide.pyodide_py.unregister_js_module; pyodide.pyodide_py.find_imports; - pyodide._module.importlib.invalidate_caches; - pyodide._module.package_loader.unpack_buffer; - pyodide._module.package_loader.get_dynlibs; - pyodide._module._util_module = pyodide.pyimport("pyodide._util"); - pyodide._module._util_module.unpack_buffer_archive; + pyodide._api.importlib.invalidate_caches; + pyodide._api.package_loader.unpack_buffer; + pyodide._api.package_loader.get_dynlibs; + pyodide._api._util_module = pyodide.pyimport("pyodide._util"); + pyodide._api._util_module.unpack_buffer_archive; pyodide.runPython(""); """ ) @@ -274,19 +274,19 @@ class SeleniumWrapper: @property def force_test_fail(self) -> bool: - return self.run_js("return !!pyodide._module.fail_test;") + return self.run_js("return !!pyodide._api.fail_test;") def clear_force_test_fail(self): - self.run_js("pyodide._module.fail_test = false;") + self.run_js("pyodide._api.fail_test = false;") def save_state(self): - self.run_js("self.__savedState = pyodide._module.saveState();") + self.run_js("self.__savedState = pyodide._api.saveState();") def restore_state(self): self.run_js( """ if(self.__savedState){ - pyodide._module.restoreState(self.__savedState) + pyodide._api.restoreState(self.__savedState) } """ ) diff --git a/packages/micropip/src/micropip/_micropip.py b/packages/micropip/src/micropip/_micropip.py index da046b4d3..236433f3e 100644 --- a/packages/micropip/src/micropip/_micropip.py +++ b/packages/micropip/src/micropip/_micropip.py @@ -35,7 +35,7 @@ else: WHEEL_BASE = Path(tempfile.mkdtemp()) if IN_BROWSER: - BUILTIN_PACKAGES = pyodide_js._module.packages.to_py() + BUILTIN_PACKAGES = pyodide_js._api.packages.to_py() else: BUILTIN_PACKAGES = {} diff --git a/pyodide-build/pyodide_build/testing.py b/pyodide-build/pyodide_build/testing.py index 0adad2362..a3426e8dd 100644 --- a/pyodide-build/pyodide_build/testing.py +++ b/pyodide-build/pyodide_build/testing.py @@ -83,7 +83,7 @@ def run_in_pyodide( eval_code.callKwargs( {{ source : atob({encoded}.join("")), - globals : pyodide._module.globals, + globals : pyodide._api.globals, filename : {filename!r} }} ); diff --git a/src/core/error_handling.c b/src/core/error_handling.c index 4da372ad2..570c32041 100644 --- a/src/core/error_handling.c +++ b/src/core/error_handling.c @@ -46,8 +46,7 @@ set_error(PyObject* err) * err - The error object */ EM_JS_REF(JsRef, new_error, (const char* msg, PyObject* err), { - return Module.hiwire.new_value( - new Module.PythonError(UTF8ToString(msg), err)); + return Module.hiwire.new_value(new API.PythonError(UTF8ToString(msg), err)); }); /** @@ -130,7 +129,7 @@ finally: return success; } -EM_JS(void, fail_test, (), { Module.fail_test = true; }) +EM_JS(void, fail_test, (), { API.fail_test = true; }) /** * Calls traceback.format_exception(type, value, traceback) and joins the @@ -217,7 +216,7 @@ EM_JS(void, log_python_error, (JsRef jserror), { let msg = Module.hiwire.get_value(jserror).message; console.warn("Python exception:\n" + msg + "\n"); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } }); diff --git a/src/core/error_handling.ts b/src/core/error_handling.ts index 83e7c40d2..69b13101b 100644 --- a/src/core/error_handling.ts +++ b/src/core/error_handling.ts @@ -1,5 +1,5 @@ import ErrorStackParser from "error-stack-parser"; -import { Module } from "./module.js"; +import { Module, API } from "./module.js"; function isPyodideFrame(frame: ErrorStackParser.StackFrame): boolean { const fileName = frame.fileName || ""; @@ -16,7 +16,7 @@ function isPyodideFrame(frame: ErrorStackParser.StackFrame): boolean { if (funcName.startsWith("Object.")) { funcName = funcName.slice("Object.".length); } - if (funcName in Module.public_api && funcName !== "PythonError") { + if (funcName in API.public_api && funcName !== "PythonError") { frame.functionName = funcName; return false; } @@ -42,7 +42,7 @@ Module.handle_js_error = function (e: any) { return; } let restored_error = false; - if (e instanceof Module.PythonError) { + if (e instanceof API.PythonError) { // Try to restore the original Python exception. restored_error = Module._restore_sys_last_exception(e.__error_address); } @@ -114,7 +114,7 @@ export class PythonError extends Error { this.__error_address = error_address; } } -Module.PythonError = PythonError; +API.PythonError = PythonError; // A special marker. If we call a CPython API from an EM_JS function and the // CPython API sets an error, we might want to return an error status back to // C keeping the current Python error flag. This signals to the EM_JS wrappers @@ -122,7 +122,7 @@ Module.PythonError = PythonError; // appropriate error value (either NULL or -1). class _PropagatePythonError extends Error { constructor() { - Module.fail_test = true; + API.fail_test = true; super( "If you are seeing this message, an internal Pyodide error has " + "occurred. Please report it to the Pyodide maintainers." diff --git a/src/core/hiwire.c b/src/core/hiwire.c index 5aa933b5a..fe2d1433a 100644 --- a/src/core/hiwire.c +++ b/src/core/hiwire.c @@ -106,7 +106,7 @@ EM_JS_NUM(int, hiwire_init, (), { Module.hiwire.get_value = function(idval) { if (!idval) { - Module.fail_test = true; + API.fail_test = true; // clang-format off // This might have happened because the error indicator is set. Let's // check. @@ -627,7 +627,7 @@ EM_JS(bool, hiwire_get_bool, (JsRef idobj), { }); EM_JS(bool, hiwire_is_pyproxy, (JsRef idobj), { - return Module.isPyProxy(Module.hiwire.get_value(idobj)); + return API.isPyProxy(Module.hiwire.get_value(idobj)); }); EM_JS(bool, hiwire_is_function, (JsRef idobj), { @@ -638,7 +638,7 @@ EM_JS(bool, hiwire_is_function, (JsRef idobj), { EM_JS(bool, hiwire_is_comlink_proxy, (JsRef idobj), { let value = Module.hiwire.get_value(idobj); - return !!(Module.Comlink && value[Module.Comlink.createEndpoint]); + return !!(API.Comlink && value[API.Comlink.createEndpoint]); }); EM_JS(bool, hiwire_is_error, (JsRef idobj), { diff --git a/src/core/js2python.js b/src/core/js2python.js index d929b7e80..be78d07ae 100644 --- a/src/core/js2python.js +++ b/src/core/js2python.js @@ -113,7 +113,7 @@ JS_FILE(js2python_init, () => { return __js2python_true(); } else if (value === false) { return __js2python_false(); - } else if (Module.isPyProxy(value)) { + } else if (API.isPyProxy(value)) { return __js2python_pyproxy(Module.PyProxy_getPtr(value)); } return undefined; diff --git a/src/core/jsproxy.c b/src/core/jsproxy.c index 1830914c5..431bd593d 100644 --- a/src/core/jsproxy.c +++ b/src/core/jsproxy.c @@ -1176,7 +1176,7 @@ EM_JS_REF(JsRef, get_async_js_call_done_callback, (JsRef proxies_id), { for (let px of proxies) { Module.pyproxy_destroy(px, msg); } - if (Module.isPyProxy(result)) { + if (API.isPyProxy(result)) { Module.pyproxy_destroy(result, msg); } }); diff --git a/src/core/keyboard_interrupt.c b/src/core/keyboard_interrupt.c index e8e8f11c3..5e92db368 100644 --- a/src/core/keyboard_interrupt.c +++ b/src/core/keyboard_interrupt.c @@ -13,8 +13,8 @@ pyodide_callback(void) if (callback_clock == 0) { callback_clock = 50; int interrupt_buffer = EM_ASM_INT({ - let result = Module.interrupt_buffer[0]; - Module.interrupt_buffer[0] = 0; + let result = API.interrupt_buffer[0]; + API.interrupt_buffer[0] = 0; return result; }); if (interrupt_buffer == 2) { diff --git a/src/core/main.c b/src/core/main.c index 798115518..1efe5dc80 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -158,7 +158,7 @@ pyodide_init(void) if (_pyodide_proxy == NULL) { FATAL_ERROR("Failed to create _pyodide proxy."); } - EM_ASM({ Module._pyodide = Module.hiwire.pop_value($0); }, _pyodide_proxy); + EM_ASM({ API._pyodide = Module.hiwire.pop_value($0); }, _pyodide_proxy); Py_CLEAR(_pyodide); Py_CLEAR(core_module); diff --git a/src/core/pre.js b/src/core/pre.js new file mode 100644 index 000000000..563d59869 --- /dev/null +++ b/src/core/pre.js @@ -0,0 +1 @@ +let API = Module.API; diff --git a/src/core/pyproxy.c b/src/core/pyproxy.c index 63cb4a3f0..5cd72ba15 100644 --- a/src/core/pyproxy.c +++ b/src/core/pyproxy.c @@ -22,7 +22,7 @@ EM_JS(int, pyproxy_Check, (JsRef x), { return false; } let val = Module.hiwire.get_value(x); - return Module.isPyProxy(val); + return API.isPyProxy(val); }); EM_JS(void, destroy_proxies, (JsRef proxies_id, char* msg_ptr), { diff --git a/src/core/pyproxy.ts b/src/core/pyproxy.ts index f39167708..65b5bb323 100644 --- a/src/core/pyproxy.ts +++ b/src/core/pyproxy.ts @@ -14,7 +14,7 @@ * See Makefile recipe for src/js/pyproxy.gen.ts */ -import { Module } from "./module.js"; +import { Module, API } from "./module.js"; // pyodide-skip @@ -50,7 +50,7 @@ declare function DEREF_U32(ptr: number, offset: number): number; export function isPyProxy(jsobj: any): jsobj is PyProxy { return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === "PyProxy"; } -Module.isPyProxy = isPyProxy; +API.isPyProxy = isPyProxy; if (globalThis.FinalizationRegistry) { Module.finalizationRegistry = new FinalizationRegistry(([ptr, cache]) => { @@ -61,7 +61,7 @@ if (globalThis.FinalizationRegistry) { } catch (e) { // I'm not really sure what happens if an error occurs inside of a // finalizer... - Module.fatal_error(e); + API.fatal_error(e); } }); // For some unclear reason this code screws up selenium FirefoxDriver. Works @@ -73,7 +73,7 @@ if (globalThis.FinalizationRegistry) { // Module._PyBuffer_Release(ptr); // Module._PyMem_Free(ptr); // } catch (e) { - // Module.fatal_error(e); + // API.fatal_error(e); // } // }); } else { @@ -267,7 +267,7 @@ Module.pyproxy_destroy = function (proxy: PyProxy, destroyed_msg: string) { Module._Py_DecRef(ptrobj); trace_pyproxy_dealloc(proxy); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } }; @@ -296,7 +296,7 @@ Module.callPyObjectKwargs = function (ptrobj: number, ...jsargs: any) { num_kwargs ); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idargs); Module.hiwire.decref(idkwnames); @@ -357,7 +357,7 @@ export class PyProxyClass { try { jsref_repr = Module.__pyproxy_repr(ptrobj); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } if (jsref_repr === 0) { Module._pythonexc2js(); @@ -448,7 +448,7 @@ export class PyProxyClass { dict_converter_id ); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(proxies_id); Module.hiwire.decref(dict_converter_id); @@ -540,7 +540,7 @@ export class PyProxyLengthMethods { try { length = Module._PyObject_Size(ptrobj); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } if (length === -1) { Module._pythonexc2js(); @@ -569,7 +569,7 @@ export class PyProxyGetItemMethods { try { idresult = Module.__pyproxy_getitem(ptrobj, idkey); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -604,7 +604,7 @@ export class PyProxySetItemMethods { try { errcode = Module.__pyproxy_setitem(ptrobj, idkey, idval); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); Module.hiwire.decref(idval); @@ -627,7 +627,7 @@ export class PyProxySetItemMethods { try { errcode = Module.__pyproxy_delitem(ptrobj, idkey); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -657,7 +657,7 @@ export class PyProxyContainsMethods { try { result = Module.__pyproxy_contains(ptrobj, idkey); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -691,7 +691,7 @@ function* iter_helper(iterptr: number, token: {}): Generator { yield Module.hiwire.pop_value(item); } } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.finalizationRegistry.unregister(token); Module._Py_DecRef(iterptr); @@ -725,7 +725,7 @@ export class PyProxyIterableMethods { try { iterptr = Module._PyObject_GetIter(ptrobj); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } if (iterptr === 0) { Module._pythonexc2js(); @@ -777,7 +777,7 @@ export class PyProxyIteratorMethods { idresult = Module.__pyproxyGen_FetchStopIterationValue(); } } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idarg); } @@ -800,7 +800,7 @@ function python_hasattr(jsobj: PyProxyClass, jskey: any) { try { result = Module.__pyproxy_hasattr(ptrobj, idkey); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -821,7 +821,7 @@ function python_getattr(jsobj: PyProxyClass, jskey: any) { try { idresult = Module.__pyproxy_getattr(ptrobj, idkey, cacheId); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -841,7 +841,7 @@ function python_setattr(jsobj: PyProxyClass, jskey: any, jsval: any) { try { errcode = Module.__pyproxy_setattr(ptrobj, idkey, idval); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); Module.hiwire.decref(idval); @@ -858,7 +858,7 @@ function python_delattr(jsobj: PyProxyClass, jskey: any) { try { errcode = Module.__pyproxy_delattr(ptrobj, idkey); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(idkey); } @@ -947,7 +947,7 @@ let PyProxyHandlers = { try { idresult = Module.__pyproxy_ownKeys(ptrobj); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } if (idresult === 0) { Module._pythonexc2js(); @@ -996,7 +996,7 @@ export class PyProxyAwaitableMethods { reject_handle_id ); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } finally { Module.hiwire.decref(reject_handle_id); Module.hiwire.decref(resolve_handle_id); @@ -1183,7 +1183,7 @@ export class PyProxyBufferMethods { try { errcode = Module.__pyproxy_get_buffer(buffer_struct_ptr, this_ptr); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } if (errcode === -1) { Module._pythonexc2js(); @@ -1277,7 +1277,7 @@ export class PyProxyBufferMethods { Module._PyBuffer_Release(view_ptr); Module._PyMem_Free(view_ptr); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } } } @@ -1465,7 +1465,7 @@ export class PyBuffer { Module._PyBuffer_Release(this._view_ptr); Module._PyMem_Free(this._view_ptr); } catch (e) { - Module.fatal_error(e); + API.fatal_error(e); } this._released = true; this.data = null; diff --git a/src/js/api.ts b/src/js/api.ts index ef11c61bf..b90c104f0 100644 --- a/src/js/api.ts +++ b/src/js/api.ts @@ -1,4 +1,4 @@ -import { Module } from "./module.js"; +import { Module, API } from "./module.js"; import { loadPackage, loadedPackages } from "./load-package"; import { isPyProxy, @@ -52,11 +52,11 @@ export let version: string = ""; // actually defined in loadPyodide (see pyodide */ export function runPython( code: string, - globals: PyProxy = Module.globals + globals: PyProxy = API.globals ): Py2JsResult { - return Module.pyodide_py.eval_code(code, globals); + return API.pyodide_py.eval_code(code, globals); } -Module.runPython = runPython; +API.runPython = runPython; /** * Inspect a Python code chunk and use :js:func:`pyodide.loadPackage` to install @@ -84,7 +84,7 @@ export async function loadPackagesFromImports( messageCallback?: (msg: string) => void, errorCallback?: (err: string) => void ) { - let pyimports = Module.pyodide_py.find_imports(code); + let pyimports = API.pyodide_py.find_imports(code); let imports; try { imports = pyimports.toJs(); @@ -95,7 +95,7 @@ export async function loadPackagesFromImports( return; } - let packageNames = Module._import_name_to_package_name; + let packageNames = API._import_name_to_package_name; let packages: Set = new Set(); for (let name of imports) { if (packageNames.has(name)) { @@ -140,11 +140,11 @@ export async function loadPackagesFromImports( */ export async function runPythonAsync( code: string, - globals: PyProxy = Module.globals + globals: PyProxy = API.globals ): Promise { - return await Module.pyodide_py.eval_code_async(code, globals); + return await API.pyodide_py.eval_code_async(code, globals); } -Module.runPythonAsync = runPythonAsync; +API.runPythonAsync = runPythonAsync; /** * Registers the JavaScript object ``module`` as a JavaScript module named @@ -158,7 +158,7 @@ Module.runPythonAsync = runPythonAsync; * @param module JavaScript object backing the module */ export function registerJsModule(name: string, module: object) { - Module.pyodide_py.register_js_module(name, module); + API.pyodide_py.register_js_module(name, module); } /** @@ -166,7 +166,7 @@ export function registerJsModule(name: string, module: object) { * Necessary to enable importing Comlink proxies into Python. */ export function registerComlink(Comlink: any) { - Module._Comlink = Comlink; + API._Comlink = Comlink; } /** @@ -181,7 +181,7 @@ export function registerComlink(Comlink: any) { * @param name Name of the JavaScript module to remove */ export function unregisterJsModule(name: string) { - Module.pyodide_py.unregister_js_module(name); + API.pyodide_py.unregister_js_module(name); } /** @@ -213,7 +213,7 @@ export function toPy( case "undefined": return obj; } - if (!obj || Module.isPyProxy(obj)) { + if (!obj || API.isPyProxy(obj)) { return obj; } let obj_id = 0; @@ -272,7 +272,7 @@ export function toPy( * @returns A PyProxy for the imported module */ export function pyimport(mod_name: string): PyProxy { - return Module.importlib.import_module(mod_name); + return API.importlib.import_module(mod_name); } /** @@ -290,10 +290,10 @@ export function unpackArchive( format: string, extract_dir?: string ) { - if (!Module._util_module) { - Module._util_module = pyimport("pyodide._util"); + if (!API._util_module) { + API._util_module = pyimport("pyodide._util"); } - Module._util_module.unpack_buffer_archive.callKwargs(buffer, { + API._util_module.unpack_buffer_archive.callKwargs(buffer, { format, extract_dir, }); @@ -302,13 +302,12 @@ export function unpackArchive( /** * @private */ -Module.saveState = () => Module.pyodide_py._state.save_state(); +API.saveState = () => API.pyodide_py._state.save_state(); /** * @private */ -Module.restoreState = (state: any) => - Module.pyodide_py._state.restore_state(state); +API.restoreState = (state: any) => API.pyodide_py._state.restore_state(state); /** * Sets the interrupt buffer to be `interrupt_buffer`. This is only useful when @@ -318,7 +317,7 @@ Module.restoreState = (state: any) => * constant for SIGINT). */ export function setInterruptBuffer(interrupt_buffer: TypedArray) { - Module.interrupt_buffer = interrupt_buffer; + API.interrupt_buffer = interrupt_buffer; Module._set_pyodide_callback(!!interrupt_buffer); } @@ -331,10 +330,10 @@ export function setInterruptBuffer(interrupt_buffer: TypedArray) { * during execution of C code. */ export function checkInterrupt() { - if (Module.interrupt_buffer[0] === 2) { - Module.interrupt_buffer[0] = 0; + if (API.interrupt_buffer[0] === 2) { + API.interrupt_buffer[0] = 0; Module._PyErr_SetInterrupt(); - Module.runPython(""); + API.runPython(""); } } @@ -405,8 +404,9 @@ export function makePublicAPI(): PyodideInterface { PythonError, PyBuffer, _module: Module, + _api: API, }; - Module.public_api = namespace; + API.public_api = namespace; return namespace; } diff --git a/src/js/load-package.ts b/src/js/load-package.ts index 8a3291449..6a598ac64 100644 --- a/src/js/load-package.ts +++ b/src/js/load-package.ts @@ -1,4 +1,4 @@ -import { Module } from "./module.js"; +import { Module, API } from "./module.js"; import { IN_NODE, nodeFsPromisesMod, _loadBinaryFile } from "./compat.js"; import { PyProxy, isPyProxy } from "./pyproxy.gen"; @@ -28,13 +28,13 @@ export async function initializePackageIndex(indexURL: string) { "Loaded packages.json does not contain the expected key 'packages'." ); } - Module.packages = package_json.packages; + API.packages = package_json.packages; // compute the inverted index for imports to package names - Module._import_name_to_package_name = new Map(); - for (let name of Object.keys(Module.packages)) { - for (let import_name of Module.packages[name].imports) { - Module._import_name_to_package_name.set(import_name, name); + API._import_name_to_package_name = new Map(); + for (let name of Object.keys(API.packages)) { + for (let import_name of API.packages[name].imports) { + API._import_name_to_package_name.set(import_name, name); } } } @@ -71,7 +71,7 @@ function addPackageToLoad( if (toLoad.has(name)) { return; } - const pkg_info = Module.packages[name]; + const pkg_info = API.packages[name]; if (!pkg_info) { throw new Error(`No known package with name '${name}'`); } @@ -144,10 +144,10 @@ async function downloadPackage( ): Promise { let file_name; if (channel === DEFAULT_CHANNEL) { - if (!(name in Module.packages)) { + if (!(name in API.packages)) { throw new Error(`Internal error: no entry for package named ${name}`); } - file_name = Module.packages[name].file_name; + file_name = API.packages[name].file_name; } else { file_name = channel; } @@ -161,14 +161,19 @@ async function downloadPackage( * @private */ async function installPackage(name: string, buffer: Uint8Array) { - const pkg = Module.packages[name] || { - file_name: ".whl", - install_dir: "site", - shared_library: false, - }; + let pkg = API.packages[name]; + if (!pkg) { + pkg = { + file_name: ".whl", + install_dir: "site", + shared_library: false, + depends: [], + imports: [] as string[], + }; + } const file_name = pkg.file_name; // This Python helper function unpacks the buffer and lists out any so files therein. - const dynlibs = Module.package_loader.unpack_buffer( + const dynlibs = API.package_loader.unpack_buffer( file_name, buffer, pkg.install_dir @@ -371,7 +376,7 @@ export async function loadPackage( // We have to invalidate Python's import caches, or it won't // see the new files. - Module.importlib.invalidate_caches(); + API.importlib.invalidate_caches(); } finally { releaseLock(); } diff --git a/src/js/module.ts b/src/js/module.ts index a18f15584..1a86397eb 100644 --- a/src/js/module.ts +++ b/src/js/module.ts @@ -10,6 +10,9 @@ Module.noWasmDecoding = false; // we preload wasm using the built in plugin now Module.preloadedWasm = {}; Module.preRun = []; +export let API: any = {}; +Module.API = API; + /** * * @param stdin diff --git a/src/js/pyodide.ts b/src/js/pyodide.ts index 8ab5a1b3c..c78a680c3 100644 --- a/src/js/pyodide.ts +++ b/src/js/pyodide.ts @@ -1,7 +1,7 @@ /** * The main bootstrap code for loading pyodide. */ -import { Module, setStandardStreams, setHomeDirectory } from "./module.js"; +import { Module, setStandardStreams, setHomeDirectory, API } from "./module.js"; import { loadScript, _loadBinaryFile, initNodeModules } from "./compat.js"; import { initializePackageIndex, loadPackage } from "./load-package.js"; import { makePublicAPI, PyodideInterface } from "./api.js"; @@ -31,7 +31,7 @@ export { * * @private */ -Module.dump_traceback = function () { +API.dump_traceback = function () { const fd_stdout = 1; Module.__Py_DumpTraceback(fd_stdout, Module._PyGILState_GetThisThreadState()); }; @@ -49,7 +49,7 @@ let fatal_error_occurred = false; * @argument e {Error} The cause of the fatal error. * @private */ -Module.fatal_error = function (e: any) { +API.fatal_error = function (e: any) { if (e.pyodide_fatal_error) { return; } @@ -65,7 +65,7 @@ Module.fatal_error = function (e: any) { "Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers." ); console.error("The cause of the fatal error was:"); - if (Module.inTestHoist) { + if (API.inTestHoist) { // Test hoist won't print the error object in a useful way so convert it to // string. console.error(e.toString()); @@ -74,12 +74,12 @@ Module.fatal_error = function (e: any) { console.error(e); } try { - Module.dump_traceback(); - for (let key of Object.keys(Module.public_api)) { + API.dump_traceback(); + for (let key of Object.keys(API.public_api)) { if (key.startsWith("_") || key === "version") { continue; } - Object.defineProperty(Module.public_api, key, { + Object.defineProperty(API.public_api, key, { enumerable: true, configurable: true, get: () => { @@ -89,8 +89,8 @@ Module.fatal_error = function (e: any) { }, }); } - if (Module.on_fatal) { - Module.on_fatal(e); + if (API.on_fatal) { + API.on_fatal(e); } } catch (err2) { console.error("Another error occurred while handling the fatal error:"); @@ -105,8 +105,8 @@ let runPythonInternal_dict: PyProxy; // Initialized in finalizeBootstrap * `eval_code` from `_pyodide` so that it can work before `pyodide` is imported. * @private */ -Module.runPythonInternal = function (code: string): Py2JsResult { - return Module._pyodide._base.eval_code(code, runPythonInternal_dict); +API.runPythonInternal = function (code: string): Py2JsResult { + return API._pyodide._base.eval_code(code, runPythonInternal_dict); }; /** @@ -178,20 +178,24 @@ function finalizeBootstrap(config: ConfigType) { // First make internal dict so that we can use runPythonInternal. // runPythonInternal uses a separate namespace, so we don't pollute the main // environment with variables from our setup. - runPythonInternal_dict = Module._pyodide._base.eval_code("{}"); - Module.importlib = Module.runPythonInternal("import importlib; importlib"); - let import_module = Module.importlib.import_module; + runPythonInternal_dict = API._pyodide._base.eval_code("{}") as PyProxy; + API.importlib = API.runPythonInternal("import importlib; importlib"); + let import_module = API.importlib.import_module; - Module.sys = import_module("sys"); - Module.sys.path.insert(0, config.homedir); + API.sys = import_module("sys"); + API.sys.path.insert(0, config.homedir); // Set up globals - let globals = Module.runPythonInternal("import __main__; __main__.__dict__"); - let builtins = Module.runPythonInternal("import builtins; builtins.__dict__"); - Module.globals = wrapPythonGlobals(globals, builtins); + let globals = API.runPythonInternal( + "import __main__; __main__.__dict__" + ) as PyProxyDict; + let builtins = API.runPythonInternal( + "import builtins; builtins.__dict__" + ) as PyProxyDict; + API.globals = wrapPythonGlobals(globals, builtins); // Set up key Javascript modules. - let importhook = Module._pyodide._importhook; + let importhook = API._pyodide._importhook; importhook.register_js_finder(); importhook.register_js_module("js", config.jsglobals); @@ -202,14 +206,14 @@ function finalizeBootstrap(config: ConfigType) { // already set up before importing pyodide_py to simplify development of // pyodide_py code (Otherwise it's very hard to keep track of which things // aren't set up yet.) - Module.pyodide_py = import_module("pyodide"); - Module.package_loader = import_module("pyodide._package_loader"); - Module.version = Module.pyodide_py.__version__; + API.pyodide_py = import_module("pyodide"); + API.package_loader = import_module("pyodide._package_loader"); + API.version = API.pyodide_py.__version__; // copy some last constants onto public API. - pyodide.pyodide_py = Module.pyodide_py; - pyodide.version = Module.version; - pyodide.globals = Module.globals; + pyodide.pyodide_py = API.pyodide_py; + pyodide.version = API.version; + pyodide.globals = API.globals; return pyodide; } @@ -292,7 +296,6 @@ export async function loadPyodide(config: { if (!config.indexURL.endsWith("/")) { config.indexURL += "/"; } - Module.indexURL = config.indexURL; await initNodeModules(); let packageIndexReady = initializePackageIndex(config.indexURL); let pyodide_py_tar_promise = _loadBinaryFile( diff --git a/src/templates/console.html b/src/templates/console.html index 6e82d3c4d..2b80a5445 100644 --- a/src/templates/console.html +++ b/src/templates/console.html @@ -150,7 +150,7 @@ term.error(s.trimEnd()); }; term.ready = Promise.resolve(); - pyodide._module.on_fatal = async (e) => { + pyodide._api.on_fatal = async (e) => { term.error( "Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers." ); diff --git a/src/tests/test_package_loading.py b/src/tests/test_package_loading.py index c820f72c7..e0ebf2dfe 100644 --- a/src/tests/test_package_loading.py +++ b/src/tests/test_package_loading.py @@ -248,7 +248,7 @@ def test_test_unvendoring(selenium_standalone): assert selenium.run_js( """ - return pyodide._module.packages['regex'].unvendored_tests; + return pyodide._api.packages['regex'].unvendored_tests; """ ) diff --git a/src/tests/test_pyodide.py b/src/tests/test_pyodide.py index d870f2969..8a88bf6d3 100644 --- a/src/tests/test_pyodide.py +++ b/src/tests/test_pyodide.py @@ -670,11 +670,11 @@ def test_restore_state(selenium): pyodide.registerJsModule("a", {somefield : 82}); pyodide.registerJsModule("b", { otherfield : 3 }); pyodide.runPython("x = 7; from a import somefield"); - let state = pyodide._module.saveState(); + let state = pyodide._api.saveState(); pyodide.registerJsModule("c", { thirdfield : 9 }); pyodide.runPython("y = 77; from b import otherfield; import c;"); - pyodide._module.restoreState(state); + pyodide._api.restoreState(state); state.destroy(); """ ) @@ -756,7 +756,7 @@ def test_fatal_error(selenium_standalone): """ assertThrows(() => pyodide.runPython, "Error", "Pyodide already fatally failed and can no longer be used.") assertThrows(() => pyodide.globals, "Error", "Pyodide already fatally failed and can no longer be used.") - assert(() => pyodide._module.runPython("1+1") === 2); + assert(() => pyodide._api.runPython("1+1") === 2); """ ) diff --git a/src/tests/test_pyproxy.py b/src/tests/test_pyproxy.py index 976afad92..69ce5c575 100644 --- a/src/tests/test_pyproxy.py +++ b/src/tests/test_pyproxy.py @@ -777,8 +777,8 @@ def test_fatal_error(selenium_standalone): selenium_standalone.run_js( """ let fatal_error = false; - let old_fatal_error = pyodide._module.fatal_error; - pyodide._module.fatal_error = (e) => { + let old_fatal_error = pyodide._api.fatal_error; + pyodide._api.fatal_error = (e) => { fatal_error = true; throw e; } @@ -838,7 +838,7 @@ def test_fatal_error(selenium_standalone): b._view_ptr = 1e10; expect_fatal(() => b.release()); } finally { - pyodide._module.fatal_error = old_fatal_error; + pyodide._api.fatal_error = old_fatal_error; } """ )