Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an argument to loadPyodide to pass environment variables #3870

Merged
merged 12 commits into from Jun 12, 2023
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
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
`{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