Add locals option to runPython and runPythonAsync (#3618)

This commit is contained in:
Hood Chatham 2023-03-03 10:09:02 +01:00 committed by GitHub
parent 99a5e21287
commit 959ceb9d57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 9 deletions

View File

@ -15,6 +15,10 @@ myst:
## Unreleased
- {{ Enhancement }} `runPython` and `runPythonAsync` now accept a `locals`
argument.
{pr}`3618`
- {{ Fix }} If the `locals` argument to `eval_code` or `eval_code_async` is
`None` it now uses `locals=globals` as the documentation says.
{pr}`3580`

View File

@ -366,10 +366,10 @@ To make your own version of {js:func}`~pyodide.runPython` you could do:
```pyodide
const my_eval_code = pyodide.runPython(`
from pyodide.code import eval_code
def my_eval_code(code, ns):
def my_eval_code(code, globals=None, locals=None):
extra_info = None
result = eval_code(code, ns)
return ns["extra_info"], result
result = eval_code(code, globals, locals)
return globals["extra_info"], result
my_eval_code
`)

View File

@ -64,17 +64,19 @@ API.runPythonInternal = function (code: string): any {
* @param options
* @param options.globals An optional Python dictionary to use as the globals.
* Defaults to :js:attr:`pyodide.globals`.
* @param options.locals An optional Python dictionary to use as the locals.
* Defaults to the same as ``globals``.
* @returns The result of the Python code translated to JavaScript. See the
* documentation for :py:func:`~pyodide.code.eval_code` for more info.
*/
export function runPython(
code: string,
options: { globals?: PyProxy } = {},
options: { globals?: PyProxy; locals?: PyProxy } = {},
): any {
if (!options.globals) {
options.globals = API.globals;
}
return API.pyodide_code.eval_code(code, options.globals);
return API.pyodide_code.eval_code(code, options.globals, options.locals);
}
API.runPython = runPython;
@ -182,17 +184,23 @@ export async function loadPackagesFromImports(
* @param options
* @param options.globals An optional Python dictionary to use as the globals.
* Defaults to :js:attr:`pyodide.globals`.
* @param options.locals An optional Python dictionary to use as the locals.
* Defaults to the same as ``globals``.
* @returns The result of the Python code translated to JavaScript.
* @async
*/
export async function runPythonAsync(
code: string,
options: { globals?: PyProxy } = {},
options: { globals?: PyProxy; locals?: PyProxy } = {},
): Promise<any> {
if (!options.globals) {
options.globals = API.globals;
}
return await API.pyodide_code.eval_code_async(code, options.globals);
return await API.pyodide_code.eval_code_async(
code,
options.globals,
options.locals,
);
}
API.runPythonAsync = runPythonAsync;

View File

@ -302,8 +302,8 @@ def test_monkeypatch_eval_code(selenium):
import pyodide
old_eval_code = pyodide.code.eval_code
x = 3
def eval_code(code, ns):
return [ns["x"], old_eval_code(code, ns)]
def eval_code(code, globals=None, locals=None):
return [globals["x"], old_eval_code(code, globals, locals)]
pyodide.code.eval_code = eval_code
"""
)
@ -584,6 +584,26 @@ def test_run_python_js_error(selenium):
)
def test_run_python_locals(selenium):
selenium.run_js(
"""
let dict = pyodide.globals.get("dict");
let locals = dict([["x", 7]]);
let globals = dict([["x", 5], ["y", 29]]);
dict.destroy();
let result = pyodide.runPython("z = 13; x + y", {locals, globals});
assert(() => locals.get("z") === 13);
assert(() => locals.has("x"));
let result2 = pyodide.runPython("del x; x + y", {locals, globals});
assert(() => !locals.has("x"));
assert(() => result === 7 + 29);
assert(() => result2 === 5 + 29);
locals.destroy();
globals.destroy();
"""
)
def test_create_once_callable(selenium):
selenium.run_js(
"""