From dcc504cd461525bb10de67292f7ba340a08be8a5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 11 Jun 2023 22:46:53 -0700 Subject: [PATCH] Add an argument to loadPyodide to pass environment variables (#3870) This also updates the command line runner to pass in all ambient environment variables except that `HOME` is set to the working directory. `homedir` is now deprecated. I added handling for `homedir` and `env.HOME`: if both are passed, raise an error, otherwise they mean the same thing. --- docs/project/changelog.md | 5 +++++ docs/project/deprecation-timeline.md | 3 +++ src/js/module.ts | 12 ++++++++--- src/js/pyodide.ts | 31 +++++++++++++++++++++++++--- src/templates/python | 2 +- src/tests/test_cmdline_runner.py | 2 +- src/tests/test_pyodide.py | 26 ++++++++++++++++++----- 7 files changed, 68 insertions(+), 13 deletions(-) diff --git a/docs/project/changelog.md b/docs/project/changelog.md index a2145bb9a74..ab1efed0a38 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -45,6 +45,11 @@ myst: new array with the result values from the iterable appended. {pr}`3904` +- {{ Enhancement }} It is now possible to pass environment variables to + `loadPyodide` via the `env` argument. `homedir` is deprecated in favor of + `{env: {HOME: whatever_directory}}`. + {pr}`3870` + ### Packages - OpenBLAS has been added and scipy now uses OpenBLAS rather than CLAPACK diff --git a/docs/project/deprecation-timeline.md b/docs/project/deprecation-timeline.md index 8cb35f0fb95..16debc942c5 100644 --- a/docs/project/deprecation-timeline.md +++ b/docs/project/deprecation-timeline.md @@ -16,6 +16,9 @@ deprecation warnings. More details about each item can often be found in the - The methods `PyProxy.supportsHas`, `PyProxy.isCallable`, etc will be removed. +- Support for the `homedir` argument will be removed in favor of + `env: {HOME: "/the/home/directory"}`. + ## 0.24.0 - The `messageCallback` and `errorCallback` argument to `loadPackage` and diff --git a/src/js/module.ts b/src/js/module.ts index 9cf70cf3244..6c77ce955bf 100644 --- a/src/js/module.ts +++ b/src/js/module.ts @@ -86,7 +86,7 @@ export function createModule(): any { * @param path The path to the home directory. * @private */ -function setHomeDirectory(Module: Module, path: string) { +function createHomeDirectory(Module: Module, path: string) { Module.preRun.push(function () { const fallbackPath = "/"; try { @@ -97,11 +97,16 @@ function setHomeDirectory(Module: Module, path: string) { console.error(`Using '${fallbackPath}' for a home directory instead`); path = fallbackPath; } - Module.ENV.HOME = path; Module.FS.chdir(path); }); } +function setEnvironment(Module: Module, env: { [key: string]: string }) { + Module.preRun.push(function () { + Object.assign(Module.ENV, env); + }); +} + /** * Mount local directories to the virtual file system. Only for Node.js. * @param module The Emscripten Module. @@ -171,7 +176,8 @@ export function initializeFileSystem(Module: Module, config: ConfigType) { } installStdlib(Module, stdLibURL); - setHomeDirectory(Module, config.homedir); + createHomeDirectory(Module, config.env.HOME); + setEnvironment(Module, config.env); mountLocalDirectories(Module, config._node_mounts); Module.preRun.push(() => initializeNativeFS(Module)); } diff --git a/src/js/pyodide.ts b/src/js/pyodide.ts index 9a779beb0de..82422db4e26 100644 --- a/src/js/pyodide.ts +++ b/src/js/pyodide.ts @@ -74,7 +74,7 @@ function finalizeBootstrap(API: any, config: ConfigType) { let import_module = API.importlib.import_module; API.sys = import_module("sys"); - API.sys.path.insert(0, config.homedir); + API.sys.path.insert(0, config.env.HOME); API.os = import_module("os"); // Set up globals @@ -174,6 +174,7 @@ export type ConfigType = { jsglobals?: object; args: string[]; _node_mounts: string[]; + env: { [key: string]: string }; }; /** @@ -204,7 +205,7 @@ export async function loadPyodide( /** * The home directory which Pyodide will use inside virtual file system. - * Default: ``"/home/pyodide"`` + * This is deprecated, use ``{env: {HOME : some_dir}}`` instead. */ homedir?: string; /** @@ -246,6 +247,17 @@ export async function loadPyodide( * more details. Default: ``[]`` */ args?: string[]; + /** + * Environment variables to pass to Python. This can be accessed inside of + * Python at runtime via `os.environ`. Certain environment variables change + * the way that Python loads: + * https://docs.python.org/3.10/using/cmdline.html#environment-variables + * Default: {} + * If `env.HOME` is undefined, it will be set to a default value of + * `"/home/pyodide"` + */ + env?: { [key: string]: string }; + /** * @ignore */ @@ -264,12 +276,25 @@ export async function loadPyodide( fullStdLib: false, jsglobals: globalThis, stdin: globalThis.prompt ? globalThis.prompt : undefined, - homedir: "/home/pyodide", lockFileURL: indexURL! + "repodata.json", args: [], _node_mounts: [], + env: {}, }; const config = Object.assign(default_config, options) as ConfigType; + if (options.homedir) { + console.warn( + "The homedir argument to loadPyodide is deprecated. " + + "Use 'env: { HOME: value }' instead of 'homedir: value'.", + ); + if (options.env && options.env.HOME) { + throw new Error("Set both env.HOME and homedir arguments"); + } + config.env.HOME = config.homedir; + } + if (!config.env.HOME) { + config.env.HOME = "/home/pyodide"; + } const Module = createModule(); Module.print = config.stdout; diff --git a/src/templates/python b/src/templates/python index 5de597fd914..652bfc7e3cc 100755 --- a/src/templates/python +++ b/src/templates/python @@ -80,9 +80,9 @@ async function main() { try { py = await loadPyodide({ args, + env: Object.assign({}, process.env, {HOME: process.cwd() }), fullStdLib: false, _node_mounts: dirsToMount(), - homedir: process.cwd(), // Strip out messages written to stderr while loading // After Pyodide is loaded we will replace stdstreams with setupStreams. stderr(e) { diff --git a/src/tests/test_cmdline_runner.py b/src/tests/test_cmdline_runner.py index bf745f56300..1d49e9c7e19 100644 --- a/src/tests/test_cmdline_runner.py +++ b/src/tests/test_cmdline_runner.py @@ -136,7 +136,7 @@ def test_invalid_cmdline_option(selenium): assert result.returncode != 0 assert result.stdout == "" assert ( - re.sub("/[/a-z]*/dist/python", "<...>/python", result.stderr) + re.sub("/.*/dist/python", "<...>/python", result.stderr) == """\ Argument expected for the -c option usage: <...>/python [option] ... [-c cmd | -m mod | file | -] [arg] ... diff --git a/src/tests/test_pyodide.py b/src/tests/test_pyodide.py index ddb4836b456..7df922e64bd 100644 --- a/src/tests/test_pyodide.py +++ b/src/tests/test_pyodide.py @@ -1307,19 +1307,35 @@ def test_custom_stdin_stdout2(selenium): def test_home_directory(selenium_standalone_noload): selenium = selenium_standalone_noload - home = "/home/custom_home" selenium.run_js( """ - let pyodide = await loadPyodide({ - homedir : "%s", + const homedir = "/home/custom_home"; + const pyodide = await loadPyodide({ + homedir, }); return pyodide.runPython(` import os - os.getcwd() == "%s" + os.getcwd() == "${homedir}" + `) + """ + ) + assert "The homedir argument to loadPyodide is deprecated" in selenium.logs + + +def test_env(selenium_standalone_noload): + selenium = selenium_standalone_noload + hashval = selenium.run_js( + """ + let pyodide = await loadPyodide({ + env : {PYTHONHASHSEED : 1}, + }); + return pyodide.runPython(` + hash((1,2,3)) `) """ - % (home, home) ) + # This may need to be updated when the Python version changes. + assert hashval == -2022708474 def test_version_variable(selenium):