Skip to content

Commit

Permalink
Add an argument to loadPyodide to pass environment variables (#3870)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hoodmane committed Jun 12, 2023
1 parent d7873bf commit dcc504c
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 13 deletions.
5 changes: 5 additions & 0 deletions docs/project/changelog.md
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions docs/project/deprecation-timeline.md
Expand Up @@ -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
Expand Down
12 changes: 9 additions & 3 deletions src/js/module.ts
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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));
}
31 changes: 28 additions & 3 deletions src/js/pyodide.ts
Expand Up @@ -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
Expand Down Expand Up @@ -174,6 +174,7 @@ export type ConfigType = {
jsglobals?: object;
args: string[];
_node_mounts: string[];
env: { [key: string]: string };
};

/**
Expand Down Expand Up @@ -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;
/**
Expand Down Expand Up @@ -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
*/
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/templates/python
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/tests/test_cmdline_runner.py
Expand Up @@ -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] ...
Expand Down
26 changes: 21 additions & 5 deletions src/tests/test_pyodide.py
Expand Up @@ -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):
Expand Down

0 comments on commit dcc504c

Please sign in to comment.