diff --git a/conftest.py b/conftest.py index c4d203c36..0f4033613 100644 --- a/conftest.py +++ b/conftest.py @@ -133,7 +133,7 @@ class SeleniumWrapper: let result = pyodide.runPython({code!r}); if(result && result.toJs){{ let converted_result = result.toJs(); - if(pyodide._module.PyProxy.isPyProxy(converted_result)){{ + if(pyodide.isPyProxy(converted_result)){{ converted_result = undefined; }} result.destroy(); @@ -149,7 +149,7 @@ class SeleniumWrapper: let result = await pyodide.runPythonAsync({code!r}); if(result && result.toJs){{ let converted_result = result.toJs(); - if(pyodide._module.PyProxy.isPyProxy(converted_result)){{ + if(pyodide.isPyProxy(converted_result)){{ converted_result = undefined; }} result.destroy(); diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 6e0a9f81d..cc529abbc 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -23,37 +23,36 @@ substitutions: - {{ Feature }} A `JsProxy` of a Javascript `Promise` or other awaitable object is now a Python awaitable. [#880](https://github.com/pyodide/pyodide/pull/880) -- {{ API }} Instead of automatically converting Python lists and dicts into Javascript, they - are now wrapped in `PyProxy`. Added a new `toJs` API to `PyProxy` to request the - conversion behavior that used to be implicit. +- {{ API }} Instead of automatically converting Python lists and dicts into + Javascript, they are now wrapped in `PyProxy`. Added a new {any}`PyProxy.toJs` + API to request the conversion behavior that used to be implicit. [#1167](https://github.com/pyodide/pyodide/pull/1167) -- {{ API }} Added `JsProxy.to_py` API to convert a Javascript object to Python. +- {{ API }} Added {any}`JsProxy.to_py` API to convert a Javascript object to Python. [#1244](https://github.com/pyodide/pyodide/pull/1244) - {{ Feature }} Flexible jsimports: it now possible to add custom Python "packages" backed by Javascript code, like the `js` package. The `js` package is now implemented using this system. [#1146](https://github.com/pyodide/pyodide/pull/1146) -- {{ Feature }} A `PyProxy` of a Python coroutine or awaitable is now an awaitable Javascript - object. Awaiting a coroutine will schedule it to run on the Python event loop - using `asyncio.ensure_future`. +- {{ Feature }} A `PyProxy` of a Python coroutine or awaitable is now an + awaitable Javascript object. Awaiting a coroutine will schedule it to run on + the Python event loop using `asyncio.ensure_future`. [#1170](https://github.com/pyodide/pyodide/pull/1170) - {{ Feature }} A `JsProxy` of a Javascript `Promise` or other awaitable object is now a Python awaitable. [#880](https://github.com/pyodide/pyodide/pull/880) -- {{ Enhancement }} Made `PyProxy` of an iterable Python object an iterable Js object: defined the - `[Symbol.iterator]` method, can be used like `for(let x of proxy)`. - Made a `PyProxy` of a Python iterator an iterator: `proxy.next()` is - translated to `next(it)`. - Made a `PyProxy` of a Python generator into a Javascript generator: - `proxy.next(val)` is translated to `gen.send(val)`. +- {{ Enhancement }} Made `PyProxy` of an iterable Python object an iterable Js + object: defined the `[Symbol.iterator]` method, can be used like `for(let x of + proxy)`. Made a `PyProxy` of a Python iterator an iterator: `proxy.next()` is + translated to `next(it)`. Made a `PyProxy` of a Python generator into a + Javascript generator: `proxy.next(val)` is translated to `gen.send(val)`. [#1180](https://github.com/pyodide/pyodide/pull/1180) - {{ API }} Updated `PyProxy` so that if the wrapped Python object supports `__getitem__` 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/pyodide/pyodide/pull/1175) -- {{ API }} The `pyodide.pyimport` function is deprecated in favor of using +- {{ API }} The {any}`pyodide.pyimport` function is deprecated in favor of using `pyodide.globals.get('key')`. [#1367](https://github.com/pyodide/pyodide/pull/1367) -- {{ API }} Added `PyProxy.getBuffer` API to allow direct access to Python +- {{ API }} Added {any}`PyProxy.getBuffer` API to allow direct access to Python buffers as Javascript TypedArrays. [1215](https://github.com/pyodide/pyodide/pull/1215) - {{ API }} The innermost level of a buffer converted to Javascript used to be a @@ -65,6 +64,8 @@ substitutions: - {{ Enhancement }} Javascript `BigInt`s are converted into Python `int` and Python `int`s larger than 2^53 are converted into `BigInt`. [1407](https://github.com/pyodide/pyodide/pull/1407) +- {{ API }} Added {any}`pyodide.isPyProxy` to test if an object is a `PyProxy`. + [1456](https://github.com/pyodide/pyodide/pull/1456) ### Fixed - {{ Fix }} getattr and dir on JsProxy now report consistent results and include all @@ -89,6 +90,9 @@ substitutions: [#1126](https://github.com/pyodide/pyodide/pull/1126) - {{ Fix }} Javascript bound method calls now work correctly with keyword arguments. [#1138](https://github.com/pyodide/pyodide/pull/1138) +- {{ Fix }} Javascript constructor calls now work correctly with keyword + arguments. + [#1433](https://github.com/pyodide/pyodide/pull/1433) ### pyodide-py package diff --git a/docs/usage/type-conversions.md b/docs/usage/type-conversions.md index 9a5466442..2ee5ebf79 100644 --- a/docs/usage/type-conversions.md +++ b/docs/usage/type-conversions.md @@ -308,13 +308,13 @@ function destroyToJsResult(x){ if(!x){ return; } - if(x.destroy){ + if(pyodide.isPyProxy(x)){ x.destroy(); return; } if(x[Symbol.iterator]){ for(let k of x){ - freeToJsResult(k); + destroyToJsResult(k); } } } diff --git a/src/core/hiwire.c b/src/core/hiwire.c index 26ce47563..802229583 100644 --- a/src/core/hiwire.c +++ b/src/core/hiwire.c @@ -515,7 +515,7 @@ EM_JS_NUM(errcode, hiwire_call_delete_method, (JsRef idobj, JsRef idkey), { }); EM_JS_NUM(bool, hiwire_is_pyproxy, (JsRef idobj), { - return Module.PyProxy.isPyProxy(Module.hiwire.get_value(idobj)); + return Module.isPyProxy(Module.hiwire.get_value(idobj)); }); EM_JS_NUM(bool, hiwire_is_function, (JsRef idobj), { diff --git a/src/core/js2python.c b/src/core/js2python.c index 082a1c88e..50d4ed2f9 100644 --- a/src/core/js2python.c +++ b/src/core/js2python.c @@ -156,8 +156,8 @@ EM_JS_NUM(errcode, js2python_init, (), { return __js2python_true(); } else if (value === false) { return __js2python_false(); - } else if (Module.PyProxy.isPyProxy(value)) { - return __js2python_pyproxy(Module.PyProxy._getPtr(value)); + } else if (Module.isPyProxy(value)) { + return __js2python_pyproxy(Module.PyProxy_getPtr(value)); } // clang-format on return 0; diff --git a/src/core/pyproxy.js b/src/core/pyproxy.js index 9f3ff1203..7a23cf6a0 100644 --- a/src/core/pyproxy.js +++ b/src/core/pyproxy.js @@ -53,12 +53,7 @@ JS_FILE(pyproxy_init_js, () => {0,0; /* Magic, see include_js_file.h */ }; // Static methods - Module.PyProxy = { - _getPtr, - isPyProxy : function(jsobj) { - return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === 'PyProxy'; - }, - }; + Module.PyProxy_getPtr = _getPtr; Module.callPyObject = function(ptrobj, ...jsargs) { let idargs = Module.hiwire.new_value(jsargs); diff --git a/src/pyodide.js b/src/pyodide.js index 7a7727998..2ccdb7486 100644 --- a/src/pyodide.js +++ b/src/pyodide.js @@ -387,6 +387,7 @@ globalThis.loadPyodide = async function(config = {}) { 'loadPackage', 'loadPackagesFromImports', 'loadedPackages', + 'isPyProxy', 'pyimport', 'runPython', 'runPythonAsync', @@ -441,7 +442,7 @@ globalThis.loadPyodide = async function(config = {}) { } // Have to do this case first because typeof(some_pyproxy) === // "function". - if (Module.PyProxy.isPyProxy(value)) { + if (Module.isPyProxy(value)) { value.destroy(); continue; } @@ -711,6 +712,15 @@ globalThis.loadPyodide = async function(config = {}) { }; // clang-format on + /** + * Is the argument a :any:`PyProxy`? + * @param jsobj {any} Object to test. + * @returns {bool} Is ``jsobj`` a :any:`PyProxy`? + */ + Module.isPyProxy = function(jsobj) { + return !!jsobj && jsobj.$$ !== undefined && jsobj.$$.type === 'PyProxy'; + }; + Module.locateFile = (path) => baseURL + path; let moduleLoaded = new Promise(r => Module.postRun = r); diff --git a/src/tests/test_typeconversions.py b/src/tests/test_typeconversions.py index fea2c3c6b..8bb7178d6 100644 --- a/src/tests/test_typeconversions.py +++ b/src/tests/test_typeconversions.py @@ -621,7 +621,7 @@ def test_python2js_with_depth(selenium): assert(Array.isArray(x), `i: ${i}, j: ${j}`); x = x[1]; } - assert(pyodide._module.PyProxy.isPyProxy(x), `i: ${i}, j: ${i}`); + assert(pyodide.isPyProxy(x), `i: ${i}, j: ${i}`); } """ ) @@ -641,7 +641,7 @@ def test_python2js_with_depth(selenium): assert(Array.isArray(x), `i: ${i}, j: ${j}`); x = x[1]; } - assert(pyodide._module.PyProxy.isPyProxy(x), `i: ${i}, j: ${i}`); + assert(pyodide.isPyProxy(x), `i: ${i}, j: ${i}`); } """ )