From 7d3c724665943fac539bc76381a937f4a6adec84 Mon Sep 17 00:00:00 2001 From: Kyle Cutler <67761731+kycutler@users.noreply.github.com> Date: Thu, 19 May 2022 11:02:52 -0700 Subject: [PATCH] Export PATH and ERRNO_CODES from Emscripten (#2582) BrowserFS can mount custom filesystems into Emscripten. However it requires the PATH and ERRNO_CODES exports from Emscripten in addition to FS. This exports `PATH` and `ERRNO_CODES` from `Module` into the `pyodide` Javascript API so they can be used with BrowserFS. --- Makefile.envs | 3 ++- docs/project/changelog.md | 3 +++ src/core/main.c | 7 ------- src/core/post.js | 5 +++++ src/js/api.ts | 20 ++++++++++++++++++++ src/js/test/integration/filesystem.test.js | 21 +++++++++++++++++++++ 6 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/core/post.js diff --git a/Makefile.envs b/Makefile.envs index f0241b915..8346e8b14 100644 --- a/Makefile.envs +++ b/Makefile.envs @@ -113,7 +113,8 @@ export MAIN_MODULE_LDFLAGS= $(LDFLAGS_BASE) \ --exclude-file "*/tests/*" \ --exclude-file "*/distutils/*" \ --pre-js src/core/pre.js \ - --pre-js src/js/_pyodide.out.js + --pre-js src/js/_pyodide.out.js \ + --post-js src/core/post.js export SIDE_MODULE_CXXFLAGS = $(CXXFLAGS_BASE) diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 964d8faaa..1e45e939a 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -86,6 +86,9 @@ substitutions: `clear_interval`, `add_event_listener` and `remove_event_listener` for the corresponding JavaScript functions. {pr}`2456` +- {{ Enhancement }} Pyodide now directly exposes the Emscripten `PATH` and `ERRNO_CODES` APIs. + {pr}`2582` + ### micropip - {{ Fix }} micropip now correctly handles package names that include dashes diff --git a/src/core/main.c b/src/core/main.c index b3fc51c62..e42bd350a 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -90,13 +90,6 @@ static struct PyModuleDef core_module_def = { int main(int argc, char** argv) { - EM_ASM({ - // For some reason emscripten doesn't make UTF8ToString available on Module - // by default... - Module.UTF8ToString = UTF8ToString; - Module.wasmTable = wasmTable; - }); - // This exits and prints a message to stderr on failure, // no status code to check. initialize_python(); diff --git a/src/core/post.js b/src/core/post.js new file mode 100644 index 000000000..021555cfd --- /dev/null +++ b/src/core/post.js @@ -0,0 +1,5 @@ +// Emscripten doesn't make UTF8ToString or wasmTable available on Module by default... +Module.UTF8ToString = UTF8ToString; +Module.wasmTable = wasmTable; +// Emscripten has a bug where it accidentally exposes an empty object as Module.ERRNO_CODES +Module.ERRNO_CODES = ERRNO_CODES; diff --git a/src/js/api.ts b/src/js/api.ts index 000510104..f549e598a 100644 --- a/src/js/api.ts +++ b/src/js/api.ts @@ -449,6 +449,8 @@ export function checkInterrupt() { export type PyodideInterface = { globals: typeof globals; FS: typeof FS; + PATH: typeof PATH; + ERRNO_CODES: typeof ERRNO_CODES; pyodide_py: typeof pyodide_py; version: typeof version; loadPackage: typeof loadPackage; @@ -486,14 +488,32 @@ export type PyodideInterface = { */ export let FS: any; +/** + * An alias to the `Emscripten Path API + * `_. + * + * This provides a variety of operations for working with file system paths, such as + * ``dirname``, ``normalize``, and ``splitPath``. + */ +export let PATH: any; + +/** + * An alias to the Emscripten ERRNO_CODES map of standard error codes. + */ +export let ERRNO_CODES: any; + /** * @private */ API.makePublicAPI = function (): PyodideInterface { FS = Module.FS; + PATH = Module.PATH; + ERRNO_CODES = Module.ERRNO_CODES; let namespace = { globals, FS, + PATH, + ERRNO_CODES, pyodide_py, version, loadPackage, diff --git a/src/js/test/integration/filesystem.test.js b/src/js/test/integration/filesystem.test.js index 50a97c6be..a0c82218a 100644 --- a/src/js/test/integration/filesystem.test.js +++ b/src/js/test/integration/filesystem.test.js @@ -25,3 +25,24 @@ describe("FS", () => { chai.assert.isTrue(result); }); }); + +describe("PATH", () => { + it("exists", async () => { + chai.assert.exists(pyodide.PATH); + }); + it("has expected keys", async () => { + chai.assert.exists(pyodide.PATH.dirname); + chai.assert.exists(pyodide.PATH.normalize); + }); +}); + +describe("ERRNO_CODES", () => { + it("exists", async () => { + chai.assert.exists(pyodide.ERRNO_CODES); + }); + it("has expected keys", async () => { + chai.assert.exists(pyodide.ERRNO_CODES.ENOENT); + chai.assert.exists(pyodide.ERRNO_CODES.EPERM); + chai.assert.exists(pyodide.ERRNO_CODES.EEXIST); + }); +});