Deprecate pyodide.pyimport (#1367)

This commit is contained in:
Roman Yurchak 2021-03-24 12:05:00 +01:00 committed by GitHub
parent e408dede25
commit f2d6137673
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 92 additions and 73 deletions

View File

@ -51,6 +51,8 @@ substitutions:
access, then the wrapper has `get`, `set`, `has`, and `delete` methods which do
`obj[key]`, `obj[key] = val`, `key in obj` and `del obj[key]` respectively.
[#1175](https://github.com/iodide-project/pyodide/pull/1175)
- {{ API }} The `pyodide.pyimport` function is deprecated in favor of using
`pyodide.globals.get('key')`. [#1367](https://github.com/iodide-project/pyodide/pull/1367)
### Fixed
- {{ Fix }} getattr and dir on JsProxy now report consistent results and include all

View File

@ -125,19 +125,18 @@ Create and save a test `index.html` page with the following contents:
## Accessing Python scope from Javascript
You can also access from Javascript all functions and variables defined in
Python by using the {any}`pyodide.pyimport` api or the {any}`pyodide.globals`
object.
Python by using the {any}`pyodide.globals` object.
For example, if you run the code `x = numpy.ones([3,3])` in Python, you can
access the variable ``x`` from Javascript in your browser's developer console
as either `pyodide.globals.get("x")` or `pyodide.pyimport('x')`. The same goes
as `pyodide.globals.get("x")`. The same goes
for functions and imports. See {ref}`type-translations` for more details.
You can try it yourself in the browser console:
```js
pyodide.runPython(`import numpy`);
pyodide.runPython(`x=numpy.ones((3, 4))`);
pyodide.globals.get('x').toJs(); // or pyodide.pyimport('x').toJs();
pyodide.globals.get('x').toJs();
// >>> [ Float64Array(4), Float64Array(4), Float64Array(4) ]
// create the same 3x4 ndarray from js

View File

@ -16,7 +16,7 @@ converted using the explicit conversion methods `JsProxy.to_py` and
Python to Javascript translations occur:
- when returning the final expression from a {any}`pyodide.runPython` call,
- when using {any}`pyodide.pyimport`,
- when using `pyodide.globals.get('key')`,
- when passing arguments to a Javascript function called from Python,
- when returning the results of a Python function called from Javascript,
- when accessing an attribute of a `PyProxy`
@ -195,7 +195,7 @@ object exist in Python either, then the Python garbage collector can eventually
collect it.
```javascript
let foo = pyodide.pyimport('foo');
let foo = pyodide.globals.get('foo');
foo();
foo.destroy();
foo(); // throws Error: Object has already been destroyed
@ -220,7 +220,7 @@ pyodide.runPython(`
import sys
print(sys.getrefcount(d)) # prints 2
`);
let d = pyodide.pyimport("d");
let d = pyodide.globals.get("d");
// Leak three temporary bound "get" methods!
let l = [d.get("a", 0), d.get("b", 0), d.get("c", 0)];
d.destroy(); // Try to free dict
@ -232,7 +232,7 @@ pyodide.runPython(`
```
Here is how we can do this without leaking:
```pyodide
let d = pyodide.pyimport("d");
let d = pyodide.globals.get("d");
let d_get = d.get; // this time avoid the leak
let l = [d_get("a", 0), d_get("b", 0), d_get("c", 0)];
d.destroy();
@ -247,7 +247,7 @@ Another exciting inconsistency is that `d.set` is a __Javascript__ method not a
PyProxy of a bound method, so using it has no effect on refcounts or memory
reclamation and it cannot be destroyed.
```pyodide
let d = pyodide.pyimport("d");
let d = pyodide.globals.get("d");
let d_set = d.set;
d_set("x", 7);
pyodide.runPython(`
@ -379,11 +379,11 @@ implementation for Javascript.
## Importing Python objects into Javascript
A Python object in the `__main__` global scope can imported into Javascript
using the {any}`pyodide.pyimport` function. Given the name of the Python object
to import, `pyimport` returns the object translated to Javascript.
using the `pyodide.globals.get` method. Given the name of the Python object
to import, it returns the object translated to Javascript.
```js
let sys = pyodide.pyimport('sys');
let sys = pyodide.globals.get('sys');
```
As always, if the result is a `PyProxy` and you care about not leaking the Python

View File

@ -2,12 +2,14 @@ def test_numpy(selenium):
selenium.load_package("numpy")
selenium.run("import numpy")
selenium.run("x = numpy.ones((32, 64))")
assert selenium.run_js("return pyodide.pyimport('x').toJs().length == 32")
assert selenium.run_js("return pyodide.globals.get('x').toJs().length == 32")
for i in range(32):
assert selenium.run_js(f"return pyodide.pyimport('x').toJs()[{i}].length == 64")
assert selenium.run_js(
f"return pyodide.globals.get('x').toJs()[{i}].length == 64"
)
for j in range(64):
assert selenium.run_js(
f"return pyodide.pyimport('x').toJs()[{i}][{j}] == 1"
f"return pyodide.globals.get('x').toJs()[{i}][{j}] == 1"
)
@ -51,7 +53,7 @@ def test_python2js_numpy_dtype(selenium_standalone):
for k in range(2):
assert (
selenium.run_js(
f"return pyodide.pyimport('x').toJs()[{i}][{j}][{k}]"
f"return pyodide.globals.get('x').toJs()[{i}][{j}][{k}]"
)
== expected_result[i][j][k]
)
@ -78,7 +80,7 @@ def test_python2js_numpy_dtype(selenium_standalone):
)
assert_equal()
classname = selenium.run_js(
"return pyodide.pyimport('x').toJs()[0][0].constructor.name"
"return pyodide.globals.get('x').toJs()[0][0].constructor.name"
)
if order == "C" and dtype not in ("uint64", "int64"):
# Here we expect a TypedArray subclass, such as Uint8Array, but
@ -94,7 +96,7 @@ def test_python2js_numpy_dtype(selenium_standalone):
)
assert_equal()
classname = selenium.run_js(
"return pyodide.pyimport('x').toJs()[0][0].constructor.name"
"return pyodide.globals.get('x').toJs()[0][0].constructor.name"
)
if order == "C" and dtype in ("int8", "uint8"):
# Here we expect a TypedArray subclass, such as Uint8Array, but
@ -147,7 +149,7 @@ def test_python2js_numpy_scalar(selenium_standalone):
assert (
selenium.run_js(
"""
return pyodide.pyimport('x') == 1
return pyodide.globals.get('x') == 1
"""
)
is True
@ -160,7 +162,7 @@ def test_python2js_numpy_scalar(selenium_standalone):
assert (
selenium.run_js(
"""
return pyodide.pyimport('x') == 1
return pyodide.globals.get('x') == 1
"""
)
is True
@ -176,7 +178,7 @@ def test_runpythonasync_numpy(selenium_standalone):
)
for i in range(5):
assert selenium_standalone.run_js(
f"return pyodide.pyimport('x').toJs()[{i}] == 0"
f"return pyodide.globals.get('x').toJs()[{i}] == 0"
)

View File

@ -435,9 +435,8 @@ globalThis.languagePluginLoader = (async () => {
*
* An alias to the global Python namespace.
*
* An object whose attributes are members of the Python global namespace. This
* is an alternative to :meth:`pyimport`. For example, to access the ``foo``
* Python object from Javascript use
* An object whose attributes are members of the Python global namespace.
* For example, to access the ``foo`` Python object from Javascript use
* ``pyodide.globals.get("foo")``
*
* @type {PyProxy}
@ -543,12 +542,21 @@ globalThis.languagePluginLoader = (async () => {
/**
* Access a Python object in the global namespace from Javascript.
*
* Note: this function is deprecated and will be removed in version 0.18.0.
* Use pyodide.globals.get('key') instead.
*
* @param {string} name Python variable name
* @returns If the Python object is an immutable type (string, number,
* boolean), it is converted to Javascript and returned. For other types, a
* ``PyProxy`` object is returned.
*/
Module.pyimport = name => Module.globals.get(name);
Module.pyimport = name => {
console.warn(
"Access to the Python global namespace via pyodide.pyimport is deprecated and " +
"will be removed in version 0.18.0. Use pyodide.globals.get('key') instead.");
return Module.globals.get(name);
};
/**
* Runs Python code, possibly asynchronously loading any known packages that

View File

@ -18,7 +18,7 @@
<body>
<script>
languagePluginLoader.then(() => {
let namespace = pyodide.pyimport("dict")();
let namespace = pyodide.globals.get("dict")();
pyodide.runPython(`
import sys
import js

View File

@ -12,7 +12,7 @@ def test_pyproxy(selenium):
f = Foo()
"""
)
selenium.run_js("window.f = pyodide.pyimport('f')")
selenium.run_js("window.f = pyodide.globals.get('f')")
assert selenium.run_js("return f.type") == "Foo"
assert selenium.run_js("return f.get_value(2)") == 128
assert selenium.run_js("return f.bar") == 42
@ -53,9 +53,11 @@ def test_pyproxy(selenium):
]
)
assert selenium.run("hasattr(f, 'baz')")
selenium.run_js("delete pyodide.pyimport('f').baz")
selenium.run_js("delete pyodide.globals.get('f').baz")
assert not selenium.run("hasattr(f, 'baz')")
assert selenium.run_js("return pyodide.pyimport('f').toString()").startswith("<Foo")
assert selenium.run_js("return pyodide.globals.get('f').toString()").startswith(
"<Foo"
)
def test_pyproxy_refcount(selenium):
@ -124,7 +126,7 @@ def test_pyproxy_destroy(selenium):
with pytest.raises(selenium.JavascriptException, match=msg):
selenium.run_js(
"""
let f = pyodide.pyimport('f');
let f = pyodide.globals.get('f');
console.assert(f.get_value(1) === 64);
f.destroy();
f.get_value();

View File

@ -35,18 +35,18 @@ def test_import_js(selenium):
assert "window" in result
def test_pyimport_multiple(selenium):
def test_globals_get_multiple(selenium):
"""See #1151"""
selenium.run("v = 0.123")
selenium.run_js("pyodide.pyimport('v')")
selenium.run_js("pyodide.pyimport('v')")
selenium.run_js("pyodide.globals.get('v')")
selenium.run_js("pyodide.globals.get('v')")
def test_pyimport_same(selenium):
def test_globals_get_same(selenium):
"""See #382"""
selenium.run("def func(): return 42")
assert selenium.run_js(
"return pyodide.pyimport('func') == pyodide.pyimport('func')"
"return pyodide.globals.get('func') == pyodide.globals.get('func')"
)
@ -72,7 +72,7 @@ def test_load_package_after_convert_string(selenium):
See #93.
"""
selenium.run("import sys\n" "x = sys.version")
selenium.run_js("let x = pyodide.pyimport('x');\n" "console.log(x);")
selenium.run_js("let x = pyodide.globals.get('x');\n" "console.log(x);")
selenium.load_package("kiwisolver")
selenium.run("import kiwisolver")

View File

@ -107,7 +107,7 @@ def test_js2python(selenium):
window.jsfalse = false;
window.jsarray0 = [];
window.jsarray1 = [1, 2, 3];
window.jspython = pyodide.pyimport("open");
window.jspython = pyodide.globals.get("open");
window.jsbytes = new Uint8Array([1, 2, 3]);
window.jsfloats = new Float32Array([1, 2, 3]);
window.jsobject = new XMLHttpRequest();
@ -270,7 +270,7 @@ def test_recursive_list_to_js(selenium_standalone):
x.append(x)
"""
)
selenium_standalone.run_js("x = pyodide.pyimport('x').toJs();")
selenium_standalone.run_js("x = pyodide.globals.get('x').toJs();")
def test_recursive_dict_to_js(selenium_standalone):
@ -280,7 +280,7 @@ def test_recursive_dict_to_js(selenium_standalone):
x[0] = x
"""
)
selenium_standalone.run_js("x = pyodide.pyimport('x').toJs();")
selenium_standalone.run_js("x = pyodide.globals.get('x').toJs();")
def test_list_js2py2js(selenium):
@ -408,7 +408,7 @@ def test_python2js_with_depth(selenium):
assert selenium.run_js(
"""
pyodide.runPython("a = [1, 2, 3]");
let res = pyodide.pyimport("a").toJs();
let res = pyodide.globals.get("a").toJs();
return (Array.isArray(res)) && JSON.stringify(res) === "[1,2,3]";
"""
)
@ -416,7 +416,7 @@ def test_python2js_with_depth(selenium):
assert selenium.run_js(
"""
pyodide.runPython("a = (1, 2, 3)");
let res = pyodide.pyimport("a").toJs();
let res = pyodide.globals.get("a").toJs();
return (Array.isArray(res)) && JSON.stringify(res) === "[1,2,3]";
"""
)
@ -424,7 +424,7 @@ def test_python2js_with_depth(selenium):
assert selenium.run_js(
"""
pyodide.runPython("a = [(1,2), (3,4), [5, 6], { 2 : 3, 4 : 9}]")
let res = pyodide.pyimport("a").toJs();
let res = pyodide.globals.get("a").toJs();
return Array.isArray(res) && \
JSON.stringify(res) === `[[1,2],[3,4],[5,6],{}]` && \
JSON.stringify(Array.from(res[3].entries())) === "[[2,3],[4,9]]";
@ -444,7 +444,7 @@ def test_python2js_with_depth(selenium):
selenium.run_js(
"""
pyodide.runPython("a = [1,[2,[3,[4,[5,[6,[7]]]]]]]")
let a = pyodide.pyimport("a");
let a = pyodide.globals.get("a");
for(let i=0; i < 7; i++){
let x = a.toJs(i);
for(let j=0; j < i; j++){
@ -464,7 +464,7 @@ def test_python2js_with_depth(selenium):
throw new Error(`Assertion failed: ${msg}`);
}
}
let a = pyodide.pyimport("a");
let a = pyodide.globals.get("a");
for(let i=0; i < 7; i++){
let x = a.toJs(i);
for(let j=0; j < i; j++){
@ -484,7 +484,7 @@ def test_python2js_with_depth(selenium):
c = [b, b, b, b, b]
`);
let total_refs = pyodide._module.hiwire.num_keys();
let res = pyodide.pyimport("c").toJs();
let res = pyodide.globals.get("c").toJs();
let new_total_refs = pyodide._module.hiwire.num_keys();
assert(total_refs === new_total_refs);
assert(res[0] === res[1]);
@ -502,7 +502,7 @@ def test_python2js_with_depth(selenium):
a.append(b)
`);
let total_refs = pyodide._module.hiwire.num_keys();
let res = pyodide.pyimport("a").toJs();
let res = pyodide.globals.get("a").toJs();
let new_total_refs = pyodide._module.hiwire.num_keys();
assert(total_refs === new_total_refs);
assert(res[0][0] === "b");
@ -690,3 +690,9 @@ def test_to_py(selenium):
`);
"""
)
def test_pyimport_deprecation(selenium):
selenium.run_js("pyodide.runPython('x = 1')")
assert selenium.run_js("return pyodide.pyimport('x') === 1")
assert "pyodide.pyimport is deprecated and will be removed" in selenium.logs