mirror of https://github.com/pyodide/pyodide.git
Eliminate pyproxy leaks (#1616)
This commit is contained in:
parent
1b8e8b3486
commit
c05c1cac78
|
@ -66,7 +66,7 @@ jobs:
|
|||
command: |
|
||||
ccache -z
|
||||
# The following packages are currently used in the main pyodide test suite
|
||||
PYODIDE_PACKAGES="micropip,pyparsing,pytz,packaging,kiwisolver,Jinja2" make
|
||||
PYODIDE_PACKAGES="micropip,pyparsing,pytz,packaging,Jinja2" make
|
||||
ccache -s
|
||||
|
||||
- run:
|
||||
|
|
2
Makefile
2
Makefile
|
@ -215,5 +215,5 @@ minimal :
|
|||
|
||||
debug :
|
||||
EXTRA_CFLAGS+=" -D DEBUG_F" \
|
||||
PYODIDE_PACKAGES+=", micropip, pyparsing, pytz, packaging, kiwisolver, " \
|
||||
PYODIDE_PACKAGES+=", micropip, pyparsing, pytz, packaging, " \
|
||||
make
|
||||
|
|
13
conftest.py
13
conftest.py
|
@ -99,7 +99,10 @@ class SeleniumWrapper:
|
|||
self.javascript_setup()
|
||||
if load_pyodide:
|
||||
self.run_js(
|
||||
"window.pyodide = await loadPyodide({ indexURL : './', fullStdLib: false });"
|
||||
"""
|
||||
window.pyodide = await loadPyodide({ indexURL : './', fullStdLib: false });
|
||||
pyodide.runPython("");
|
||||
"""
|
||||
)
|
||||
self.save_state()
|
||||
self.script_timeout = script_timeout
|
||||
|
@ -362,7 +365,7 @@ def pytest_runtest_call(item):
|
|||
selenium = item.funcargs["selenium_standalone"]
|
||||
if selenium:
|
||||
trace_hiwire_refs = pytest.mark.skip_refcount_check.mark not in item.own_markers
|
||||
trace_pyproxies = pytest.mark.trace_pyproxies.mark in item.own_markers
|
||||
trace_pyproxies = pytest.mark.skip_pyproxy_check.mark not in item.own_markers
|
||||
yield from test_wrapper_check_for_memory_leaks(
|
||||
selenium, trace_hiwire_refs, trace_pyproxies
|
||||
)
|
||||
|
@ -382,12 +385,12 @@ def test_wrapper_check_for_memory_leaks(selenium, trace_hiwire_refs, trace_pypro
|
|||
# get_result (we don't want to override the error message by raising a
|
||||
# different error here.)
|
||||
a.get_result()
|
||||
if trace_hiwire_refs:
|
||||
delta_keys = selenium.get_num_hiwire_keys() - init_num_keys
|
||||
assert delta_keys == 0
|
||||
if trace_pyproxies:
|
||||
delta_proxies = selenium.get_num_proxies() - init_num_proxies
|
||||
assert delta_proxies == 0
|
||||
if trace_hiwire_refs:
|
||||
delta_keys = selenium.get_num_hiwire_keys() - init_num_keys
|
||||
assert delta_keys == 0
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
|||
|
||||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_matplotlib(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
selenium.load_package("matplotlib")
|
||||
|
@ -16,6 +17,7 @@ def test_matplotlib(selenium_standalone):
|
|||
|
||||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_svg(selenium):
|
||||
selenium.load_package("matplotlib")
|
||||
selenium.run("from matplotlib import pyplot as plt")
|
||||
|
@ -29,6 +31,7 @@ def test_svg(selenium):
|
|||
assert content.startswith("<?xml")
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pdf(selenium):
|
||||
selenium.load_package("matplotlib")
|
||||
selenium.run("from matplotlib import pyplot as plt")
|
||||
|
|
|
@ -3,17 +3,24 @@ import pytest
|
|||
|
||||
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.globals.get('x').toJs().length == 32")
|
||||
selenium.run(
|
||||
"""
|
||||
import numpy
|
||||
x = numpy.ones((32, 64))
|
||||
"""
|
||||
)
|
||||
selenium.run_js(
|
||||
"""
|
||||
let xpy = pyodide.runPython('x');
|
||||
window.x = xpy.toJs();
|
||||
xpy.destroy();
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js("return x.length === 32")
|
||||
for i in range(32):
|
||||
assert selenium.run_js(
|
||||
f"return pyodide.globals.get('x').toJs()[{i}].length == 64"
|
||||
)
|
||||
assert selenium.run_js(f"return x[{i}].length == 64")
|
||||
for j in range(64):
|
||||
assert selenium.run_js(
|
||||
f"return pyodide.globals.get('x').toJs()[{i}][{j}] == 1"
|
||||
)
|
||||
assert selenium.run_js(f"return x[{i}][{j}] == 1")
|
||||
|
||||
|
||||
def test_typed_arrays(selenium):
|
||||
|
@ -30,7 +37,6 @@ def test_typed_arrays(selenium):
|
|||
("Float32Array", "float32"),
|
||||
("Float64Array", "float64"),
|
||||
):
|
||||
print(jstype, npytype)
|
||||
selenium.run_js(f"window.array = new {jstype}([1, 2, 3, 4]);\n")
|
||||
assert selenium.run(
|
||||
"from js import array\n"
|
||||
|
@ -40,6 +46,7 @@ def test_typed_arrays(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
@pytest.mark.parametrize("order", ("C", "F"))
|
||||
@pytest.mark.parametrize(
|
||||
"dtype",
|
||||
|
@ -106,6 +113,7 @@ def test_python2js_numpy_dtype(selenium, order, dtype):
|
|||
assert selenium.run("np.array([True, False])") == [True, False]
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_py2js_buffer_clear_error_flag(selenium):
|
||||
selenium.load_package("numpy")
|
||||
selenium.run("import numpy as np")
|
||||
|
@ -119,6 +127,7 @@ def test_py2js_buffer_clear_error_flag(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
@pytest.mark.parametrize(
|
||||
"dtype",
|
||||
(
|
||||
|
@ -146,8 +155,8 @@ def test_python2js_numpy_scalar(selenium, dtype):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
return pyodide.globals.get('x') == 1
|
||||
"""
|
||||
return pyodide.globals.get('x') == 1
|
||||
"""
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
@ -166,6 +175,7 @@ def test_python2js_numpy_scalar(selenium, dtype):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_runpythonasync_numpy(selenium_standalone):
|
||||
selenium_standalone.run_async(
|
||||
"""
|
||||
|
@ -194,6 +204,7 @@ def test_runwebworker_numpy(selenium_webworker_standalone):
|
|||
assert output == "[0. 0. 0. 0. 0.]"
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_get_buffer(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
|
@ -224,6 +235,7 @@ def test_get_buffer(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
@pytest.mark.parametrize(
|
||||
"arg",
|
||||
[
|
||||
|
@ -323,6 +335,11 @@ def test_get_buffer_error_messages(selenium):
|
|||
import numpy as np
|
||||
x = np.ones(2, dtype=np.float16)
|
||||
`);
|
||||
pyodide.pyimport("x").getBuffer();
|
||||
let x = pyodide.runPython("x");
|
||||
try {
|
||||
x.getBuffer();
|
||||
} finally {
|
||||
x.destroy();
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -56,19 +56,26 @@ def run_in_pyodide(
|
|||
# containing the source. This results in a more helpful
|
||||
# traceback
|
||||
selenium.run_js(
|
||||
"""pyodide._module.pyodide_py.eval_code.callKwargs(
|
||||
"""
|
||||
let eval_code = pyodide._module.pyodide_py.eval_code;
|
||||
try {{
|
||||
eval_code.callKwargs(
|
||||
{{
|
||||
source : {!r},
|
||||
globals : pyodide._module.globals,
|
||||
filename : {!r}
|
||||
}}
|
||||
)""".format(
|
||||
)
|
||||
}} finally {{
|
||||
eval_code.destroy();
|
||||
}}
|
||||
""".format(
|
||||
_run_in_pyodide_get_source(f), inspect.getsourcefile(f)
|
||||
)
|
||||
)
|
||||
# When invoking the function, use the default filename <eval>
|
||||
selenium.run_js(
|
||||
"""pyodide._module.pyodide_py.eval_code("{}()", pyodide._module.globals)""".format(
|
||||
"""pyodide.runPython("{}()", pyodide._module.globals)""".format(
|
||||
f.__name__
|
||||
)
|
||||
)
|
||||
|
|
|
@ -3,8 +3,8 @@ norecursedirs = build cpython emsdk/
|
|||
addopts = --doctest-modules
|
||||
markers =
|
||||
skip_refcount_check: Dont run refcount checks
|
||||
skip_pyproxy_check: Dont run pyproxy allocation checks
|
||||
driver_timeout: Set script timeout in WebDriver
|
||||
trace_pyproxies: Trace pyproxy allocations
|
||||
|
||||
[bumpversion]
|
||||
current_version = 0.16.1
|
||||
|
|
|
@ -131,7 +131,16 @@ export async function loadPackagesFromImports(
|
|||
messageCallback,
|
||||
errorCallback
|
||||
) {
|
||||
let imports = Module.pyodide_py.find_imports(code).toJs();
|
||||
let find_imports = Module.pyodide_py.find_imports;
|
||||
let imports;
|
||||
let pyimports;
|
||||
try {
|
||||
pyimports = find_imports(code);
|
||||
imports = pyimports.toJs();
|
||||
} finally {
|
||||
find_imports.destroy();
|
||||
pyimports && pyimports.destroy();
|
||||
}
|
||||
if (imports.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -213,7 +222,12 @@ Module.runPythonAsync = runPythonAsync;
|
|||
* @param {object} module Javascript object backing the module
|
||||
*/
|
||||
export function registerJsModule(name, module) {
|
||||
Module.pyodide_py.register_js_module(name, module);
|
||||
let register_js_module = Module.pyodide_py.register_js_module;
|
||||
try {
|
||||
register_js_module(name, module);
|
||||
} finally {
|
||||
register_js_module.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,7 +242,12 @@ export function registerJsModule(name, module) {
|
|||
* @param {string} name Name of the Javascript module to remove
|
||||
*/
|
||||
export function unregisterJsModule(name) {
|
||||
Module.pyodide_py.unregister_js_module(name);
|
||||
let unregister_js_module = Module.pyodide_py.unregister_js_module;
|
||||
try {
|
||||
unregister_js_module(name);
|
||||
} finally {
|
||||
unregister_js_module.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,7 +16,8 @@ def test_await_jsproxy(selenium):
|
|||
global resolve
|
||||
resolve = res
|
||||
from js import Promise
|
||||
p = Promise.new(prom)
|
||||
from pyodide import create_once_callable
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
async def temp():
|
||||
x = await p
|
||||
return x + 7
|
||||
|
@ -45,6 +46,7 @@ def test_then_jsproxy(selenium):
|
|||
reject = rej
|
||||
|
||||
from js import Promise
|
||||
from pyodide import create_once_callable
|
||||
result = None
|
||||
err = None
|
||||
finally_occurred = False
|
||||
|
@ -65,7 +67,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.then(onfulfilled, onrejected)
|
||||
resolve(10)
|
||||
"""
|
||||
|
@ -81,7 +83,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.then(onfulfilled, onrejected)
|
||||
reject(10)
|
||||
"""
|
||||
|
@ -97,7 +99,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.catch(onrejected)
|
||||
resolve(10)
|
||||
"""
|
||||
|
@ -107,7 +109,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.catch(onrejected)
|
||||
reject(10)
|
||||
"""
|
||||
|
@ -122,7 +124,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.finally_(onfinally)
|
||||
resolve(10)
|
||||
"""
|
||||
|
@ -137,7 +139,7 @@ def test_then_jsproxy(selenium):
|
|||
|
||||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(prom)
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.finally_(onfinally)
|
||||
reject(10)
|
||||
"""
|
||||
|
@ -245,7 +247,8 @@ def test_eval_code_await_jsproxy(selenium):
|
|||
global resolve
|
||||
resolve = res
|
||||
from js import Promise
|
||||
p = Promise.new(prom)
|
||||
from pyodide import create_once_callable
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
from pyodide._base import eval_code_async
|
||||
c = eval_code_async(
|
||||
'''
|
||||
|
@ -350,9 +353,11 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async("1+1");
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async("1+1");
|
||||
let result = await c;
|
||||
c.destroy();
|
||||
eval_code_async.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
|
@ -363,8 +368,11 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
let finally_occurred = false;
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async("1+1");
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async("1+1");
|
||||
let result = await c.finally(() => { finally_occurred = true; });
|
||||
c.destroy();
|
||||
eval_code_async.destroy();
|
||||
return [result, finally_occurred];
|
||||
"""
|
||||
)
|
||||
|
@ -376,12 +384,15 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
"""
|
||||
let finally_occurred = false;
|
||||
let err_occurred = false;
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async("raise ValueError('hi')");
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async("raise ValueError('hi')");
|
||||
try {
|
||||
let result = await c.finally(() => { finally_occurred = true; });
|
||||
} catch(e){
|
||||
err_occurred = e.constructor.name === "PythonError";
|
||||
}
|
||||
eval_code_async.destroy();
|
||||
c.destroy();
|
||||
return [finally_occurred, err_occurred];
|
||||
"""
|
||||
)
|
||||
|
@ -390,18 +401,26 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async("raise ValueError('hi')");
|
||||
return await c.catch(e => e.constructor.name === "PythonError");
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async("raise ValueError('hi')");
|
||||
eval_code_async.destroy();
|
||||
try {
|
||||
return await c.catch(e => e.constructor.name === "PythonError");
|
||||
} finally {
|
||||
c.destroy();
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async(`
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async(`
|
||||
from js import fetch
|
||||
await (await fetch('packages.json')).json()
|
||||
`);
|
||||
let packages = await c;
|
||||
eval_code_async.destroy();
|
||||
c.destroy();
|
||||
return (!!packages.dependencies) && (!!packages.import_name_to_package_name);
|
||||
"""
|
||||
|
@ -409,8 +428,11 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let c = pyodide._module.pyodide_py._base.eval_code_async("1+1");
|
||||
let eval_code_async = pyodide.pyodide_py.eval_code_async;
|
||||
let c = eval_code_async("1+1");
|
||||
await c;
|
||||
eval_code_async.destroy();
|
||||
c.destroy();
|
||||
let err_occurred = false;
|
||||
try {
|
||||
// Triggers: cannot await already awaited coroutine
|
||||
|
@ -426,11 +448,11 @@ def test_await_pyproxy_eval_async(selenium):
|
|||
def test_await_pyproxy_async_def(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let packages = await pyodide.runPython(`
|
||||
let packages = await pyodide.runPythonAsync(`
|
||||
from js import fetch
|
||||
async def temp():
|
||||
return await (await fetch('packages.json')).json()
|
||||
temp()
|
||||
await temp()
|
||||
`);
|
||||
return (!!packages.dependencies) && (!!packages.import_name_to_package_name);
|
||||
"""
|
||||
|
|
|
@ -176,20 +176,20 @@ def test_interactive_console(selenium, safe_selenium_sys_redirections):
|
|||
|
||||
selenium.run("shell.push('x = 5')")
|
||||
selenium.run("shell.push('x')")
|
||||
selenium.run_js("await pyodide.runPython('shell.run_complete');")
|
||||
selenium.run_js("await pyodide.runPythonAsync('await shell.run_complete');")
|
||||
assert selenium.run("result") == 5
|
||||
|
||||
selenium.run("shell.push('x ** 2')")
|
||||
selenium.run_js("await pyodide.runPython('shell.run_complete');")
|
||||
selenium.run_js("await pyodide.runPythonAsync('await shell.run_complete');")
|
||||
|
||||
assert selenium.run("result") == 25
|
||||
|
||||
selenium.run("shell.push('def f(x):')")
|
||||
selenium.run("shell.push(' return x*x + 1')")
|
||||
selenium.run("shell.push('')")
|
||||
selenium.run("shell.push('[f(x) for x in range(5)]')")
|
||||
selenium.run_js("await pyodide.runPython('shell.run_complete');")
|
||||
assert selenium.run("result") == [1, 2, 5, 10, 17]
|
||||
selenium.run("shell.push('str([f(x) for x in range(5)])')")
|
||||
selenium.run_js("await pyodide.runPythonAsync('await shell.run_complete');")
|
||||
assert selenium.run("result") == str([1, 2, 5, 10, 17])
|
||||
|
||||
selenium.run("shell.push('def factorial(n):')")
|
||||
selenium.run("shell.push(' if n < 2:')")
|
||||
|
@ -198,13 +198,13 @@ def test_interactive_console(selenium, safe_selenium_sys_redirections):
|
|||
selenium.run("shell.push(' return n * factorial(n - 1)')")
|
||||
selenium.run("shell.push('')")
|
||||
selenium.run("shell.push('factorial(10)')")
|
||||
selenium.run_js("await pyodide.runPython('shell.run_complete');")
|
||||
selenium.run_js("await pyodide.runPythonAsync('await shell.run_complete');")
|
||||
assert selenium.run("result") == 3628800
|
||||
|
||||
# with package load
|
||||
selenium.run("shell.push('import pytz')")
|
||||
selenium.run("shell.push('pytz.utc.zone')")
|
||||
selenium.run_js("await pyodide.runPython('shell.run_complete');")
|
||||
selenium.run_js("await pyodide.runPythonAsync('await shell.run_complete');")
|
||||
assert selenium.run("result") == "UTC"
|
||||
|
||||
|
||||
|
|
|
@ -8,11 +8,14 @@ def test_jsproxy_dir(selenium):
|
|||
"""
|
||||
window.a = { x : 2, y : "9" };
|
||||
window.b = function(){};
|
||||
return pyodide.runPython(`
|
||||
let pyresult = pyodide.runPython(`
|
||||
from js import a
|
||||
from js import b
|
||||
[dir(a), dir(b)]
|
||||
`).toJs();
|
||||
`);
|
||||
let result = pyresult.toJs();
|
||||
pyresult.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
jsproxy_items = set(
|
||||
|
@ -67,10 +70,13 @@ def test_jsproxy_getattr(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
window.a = { x : 2, y : "9", typeof : 7 };
|
||||
return pyodide.runPython(`
|
||||
let pyresult = pyodide.runPython(`
|
||||
from js import a
|
||||
[ a.x, a.y, a.typeof ]
|
||||
`).toJs();
|
||||
`);
|
||||
let result = pyresult.toJs();
|
||||
pyresult.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
== [2, "9", "object"]
|
||||
|
@ -211,12 +217,15 @@ def test_jsproxy_call(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
window.f = function(){ return arguments.length; };
|
||||
return pyodide.runPython(
|
||||
let pyresult = pyodide.runPython(
|
||||
`
|
||||
from js import f
|
||||
[f(*range(n)) for n in range(10)]
|
||||
`
|
||||
).toJs();
|
||||
);
|
||||
let result = pyresult.toJs();
|
||||
pyresult.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
== list(range(10))
|
||||
|
@ -302,7 +311,9 @@ def test_import_invocation():
|
|||
def temp():
|
||||
print("okay?")
|
||||
|
||||
js.setTimeout(temp, 100)
|
||||
from pyodide import create_once_callable
|
||||
|
||||
js.setTimeout(create_once_callable(temp), 100)
|
||||
js.fetch("packages.json")
|
||||
|
||||
|
||||
|
@ -398,7 +409,7 @@ def test_unregister_jsmodule(selenium):
|
|||
raises = TestCase().assertRaises
|
||||
with raises(ImportError):
|
||||
import a
|
||||
`)
|
||||
`);
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -529,6 +540,7 @@ def test_mixins_feature_presence(selenium):
|
|||
}
|
||||
test_object(o, keys_expected);
|
||||
}
|
||||
test_object.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -553,7 +565,7 @@ def test_mixins_calls(selenium):
|
|||
};
|
||||
testObjects.awaitable = { then(cb){ cb(7); } };
|
||||
|
||||
let result = await pyodide.runPythonAsync(`
|
||||
let pyresult = await pyodide.runPythonAsync(`
|
||||
from js import testObjects as obj
|
||||
result = []
|
||||
result.append(["iterable1", list(iter(obj.iterable)), [3, 5, 7]])
|
||||
|
@ -577,7 +589,9 @@ def test_mixins_calls(selenium):
|
|||
result.append(["awaitable", await obj.awaitable, 7])
|
||||
result
|
||||
`);
|
||||
return result.toJs();
|
||||
let result = pyresult.toJs();
|
||||
pyresult.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
for [desc, a, b] in result:
|
||||
|
@ -779,6 +793,7 @@ def test_memory_leaks(selenium):
|
|||
from js import a
|
||||
repr(a)
|
||||
[*a]
|
||||
None
|
||||
`);
|
||||
"""
|
||||
)
|
||||
|
|
|
@ -196,7 +196,12 @@ def test_hiwire_is_promise(selenium):
|
|||
|
||||
assert not selenium.run_js(
|
||||
"""
|
||||
return pyodide._module.hiwire.isPromise(pyodide.globals);
|
||||
let d = pyodide.runPython("{}");
|
||||
try {
|
||||
return pyodide._module.hiwire.isPromise(d);
|
||||
} finally {
|
||||
d.destroy();
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -238,7 +243,6 @@ def test_run_python_async_toplevel_await(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.trace_pyproxies
|
||||
def test_run_python_proxy_leak(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
|
@ -424,6 +428,7 @@ def test_docstrings_b(selenium):
|
|||
|
||||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_restore_state(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
|
|
|
@ -2,61 +2,68 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def test_pyproxy(selenium):
|
||||
selenium.run(
|
||||
def test_pyproxy_class(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
class Foo:
|
||||
bar = 42
|
||||
def get_value(self, value):
|
||||
return value * 64
|
||||
f = Foo()
|
||||
pyodide.runPython(`
|
||||
class Foo:
|
||||
bar = 42
|
||||
def get_value(self, value):
|
||||
return value * 64
|
||||
f = Foo()
|
||||
`);
|
||||
window.f = pyodide.globals.get('f');
|
||||
assert(() => f.type === "Foo");
|
||||
let f_get_value = f.get_value
|
||||
assert(() => f_get_value(2) === 128);
|
||||
f_get_value.destroy();
|
||||
assert(() => f.bar === 42);
|
||||
assert(() => 'bar' in f);
|
||||
f.baz = 32;
|
||||
assert(() => f.baz === 32);
|
||||
pyodide.runPython(`assert hasattr(f, 'baz')`)
|
||||
window.f_props = Object.getOwnPropertyNames(f);
|
||||
delete f.baz
|
||||
pyodide.runPython(`assert not hasattr(f, 'baz')`)
|
||||
assert(() => f.toString().startsWith("<Foo"));
|
||||
f.destroy();
|
||||
"""
|
||||
)
|
||||
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
|
||||
assert selenium.run_js("return ('bar' in f)")
|
||||
selenium.run_js("f.baz = 32")
|
||||
assert selenium.run("f.baz") == 32
|
||||
assert set(selenium.run_js("return Object.getOwnPropertyNames(f)")) > set(
|
||||
[
|
||||
"__class__",
|
||||
"__delattr__",
|
||||
"__dict__",
|
||||
"__dir__",
|
||||
"__doc__",
|
||||
"__eq__",
|
||||
"__format__",
|
||||
"__ge__",
|
||||
"__getattribute__",
|
||||
"__gt__",
|
||||
"__hash__",
|
||||
"__init__",
|
||||
"__init_subclass__",
|
||||
"__le__",
|
||||
"__lt__",
|
||||
"__module__",
|
||||
"__ne__",
|
||||
"__new__",
|
||||
"__reduce__",
|
||||
"__reduce_ex__",
|
||||
"__repr__",
|
||||
"__setattr__",
|
||||
"__sizeof__",
|
||||
"__str__",
|
||||
"__subclasshook__",
|
||||
"__weakref__",
|
||||
"bar",
|
||||
"baz",
|
||||
"get_value",
|
||||
]
|
||||
)
|
||||
assert selenium.run("hasattr(f, 'baz')")
|
||||
selenium.run_js("delete pyodide.globals.get('f').baz")
|
||||
assert not selenium.run("hasattr(f, 'baz')")
|
||||
assert selenium.run_js("return pyodide.globals.get('f').toString()").startswith(
|
||||
"<Foo"
|
||||
assert (
|
||||
set(
|
||||
[
|
||||
"__class__",
|
||||
"__delattr__",
|
||||
"__dict__",
|
||||
"__dir__",
|
||||
"__doc__",
|
||||
"__eq__",
|
||||
"__format__",
|
||||
"__ge__",
|
||||
"__getattribute__",
|
||||
"__gt__",
|
||||
"__hash__",
|
||||
"__init__",
|
||||
"__init_subclass__",
|
||||
"__le__",
|
||||
"__lt__",
|
||||
"__module__",
|
||||
"__ne__",
|
||||
"__new__",
|
||||
"__reduce__",
|
||||
"__reduce_ex__",
|
||||
"__repr__",
|
||||
"__setattr__",
|
||||
"__sizeof__",
|
||||
"__str__",
|
||||
"__subclasshook__",
|
||||
"__weakref__",
|
||||
"bar",
|
||||
"baz",
|
||||
"get_value",
|
||||
]
|
||||
).difference(selenium.run_js("return f_props"))
|
||||
== set()
|
||||
)
|
||||
|
||||
|
||||
|
@ -72,6 +79,7 @@ def test_pyproxy_clone(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pyproxy_refcount(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
|
@ -139,7 +147,9 @@ def test_pyproxy_destroy(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
let f = pyodide.globals.get('f');
|
||||
assert(()=> f.get_value(1) === 64);
|
||||
let f_get_value = f.get_value;
|
||||
assert(()=> f_get_value(1) === 64);
|
||||
f_get_value.destroy();
|
||||
f.destroy();
|
||||
f.get_value();
|
||||
"""
|
||||
|
@ -155,7 +165,9 @@ def test_pyproxy_iter(selenium):
|
|||
yield i
|
||||
test()
|
||||
`);
|
||||
return [c.type, [...c]];
|
||||
let result = [c.type, [...c]];
|
||||
c.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
assert ty == "generator"
|
||||
|
@ -163,11 +175,13 @@ def test_pyproxy_iter(selenium):
|
|||
|
||||
[ty, l] = selenium.run_js(
|
||||
"""
|
||||
c = pyodide.runPython(`
|
||||
let c = pyodide.runPython(`
|
||||
from collections import ChainMap
|
||||
ChainMap({"a" : 2, "b" : 3})
|
||||
`);
|
||||
return [c.type, [...c]];
|
||||
let result = [c.type, [...c]];
|
||||
c.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
assert ty == "ChainMap"
|
||||
|
@ -189,6 +203,7 @@ def test_pyproxy_iter(selenium):
|
|||
result.push(value);
|
||||
({done, value} = c.next(value + 1));
|
||||
}
|
||||
c.destroy();
|
||||
|
||||
function* test(){
|
||||
let acc = 0;
|
||||
|
@ -336,8 +351,8 @@ def test_pyproxy_mixins(selenium):
|
|||
class AwaitIter(Await, Iter): pass
|
||||
|
||||
class AwaitNext(Await, Next): pass
|
||||
|
||||
[NoImpls(), Await(), Iter(), Next(), AwaitIter(), AwaitNext()]
|
||||
from pyodide import to_js
|
||||
to_js([NoImpls(), Await(), Iter(), Next(), AwaitIter(), AwaitNext()])
|
||||
`);
|
||||
let name_proxy = {noimpls, awaitable, iterable, iterator, awaititerable, awaititerator};
|
||||
let result = {};
|
||||
|
@ -353,6 +368,7 @@ def test_pyproxy_mixins(selenium):
|
|||
impls[name] = key in x;
|
||||
}
|
||||
result[name] = impls;
|
||||
x.destroy();
|
||||
}
|
||||
return result;
|
||||
"""
|
||||
|
@ -391,16 +407,26 @@ def test_pyproxy_mixins2(selenium):
|
|||
assert(() => get_method.prototype === undefined);
|
||||
assert(() => !("length" in get_method));
|
||||
assert(() => !("name" in get_method));
|
||||
get_method.destroy();
|
||||
|
||||
let d = pyodide.runPython("{}");
|
||||
assert(() => d.$get.type === "builtin_function_or_method");
|
||||
let d_get = d.$get;
|
||||
assert(() => d_get.type === "builtin_function_or_method");
|
||||
assert(() => d.get.type === undefined);
|
||||
assert(() => d.set.type === undefined);
|
||||
d_get.destroy();
|
||||
d.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_pyproxy_mixins3(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
let [Test, t] = pyodide.runPython(`
|
||||
class Test: pass
|
||||
[Test, Test()]
|
||||
from pyodide import to_js
|
||||
to_js([Test, Test()])
|
||||
`);
|
||||
assert(() => Test.prototype === undefined);
|
||||
assert(() => !("name" in Test));
|
||||
|
@ -426,14 +452,23 @@ def test_pyproxy_mixins2(selenium):
|
|||
|
||||
assertThrows( () => Test.$$ = 7, "TypeError", /^Cannot set read only field/);
|
||||
assertThrows( () => delete Test.$$, "TypeError", /^Cannot delete read only field/);
|
||||
Test.destroy();
|
||||
t.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_pyproxy_mixins4(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
[Test, t] = pyodide.runPython(`
|
||||
class Test:
|
||||
caller="fifty"
|
||||
prototype="prototype"
|
||||
name="me"
|
||||
length=7
|
||||
[Test, Test()]
|
||||
from pyodide import to_js
|
||||
to_js([Test, Test()])
|
||||
`);
|
||||
assert(() => Test.$prototype === "prototype");
|
||||
assert(() => Test.prototype === undefined);
|
||||
|
@ -444,20 +479,38 @@ def test_pyproxy_mixins2(selenium):
|
|||
assert(() => t.prototype === "prototype");
|
||||
assert(() => t.name==="me");
|
||||
assert(() => t.length === 7);
|
||||
Test.destroy();
|
||||
t.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_pyproxy_mixins5(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
[Test, t] = pyodide.runPython(`
|
||||
class Test:
|
||||
def __len__(self):
|
||||
return 9
|
||||
[Test, Test()]
|
||||
from pyodide import to_js
|
||||
to_js([Test, Test()])
|
||||
`);
|
||||
assert(() => !("length" in Test));
|
||||
assert(() => t.length === 9);
|
||||
t.length = 10;
|
||||
assert(() => t.length === 9);
|
||||
assert(() => t.$length === 10);
|
||||
let t__len__ = t.__len__;
|
||||
assert(() => t__len__() === 9);
|
||||
t__len__.destroy();
|
||||
Test.destroy();
|
||||
t.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_pyproxy_mixins6(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
let l = pyodide.runPython(`
|
||||
l = [5, 6, 7] ; l
|
||||
`);
|
||||
|
@ -473,10 +526,12 @@ def test_pyproxy_mixins2(selenium):
|
|||
assert len(l) == 2 and l[1] == 7
|
||||
`);
|
||||
assert(() => l.length === 2 && l.get(1) === 7);
|
||||
l.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pyproxy_gc(selenium):
|
||||
if selenium.browser != "chrome":
|
||||
pytest.skip("No gc exposed")
|
||||
|
@ -526,6 +581,7 @@ def test_pyproxy_gc(selenium):
|
|||
d.get();
|
||||
get_ref_count(2);
|
||||
d.get();
|
||||
d.destroy()
|
||||
"""
|
||||
)
|
||||
selenium.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})
|
||||
|
@ -541,6 +597,7 @@ def test_pyproxy_gc(selenium):
|
|||
assert dict(a) == {0: 2, 1: 3, 2: 4, 3: 2, "destructor_ran": True}
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pyproxy_gc_destroy(selenium):
|
||||
if selenium.browser != "chrome":
|
||||
pytest.skip("No gc exposed")
|
||||
|
@ -604,8 +661,9 @@ def test_pyproxy_copy(selenium):
|
|||
let a = pyodide.runPython(`d = { 1 : 2}; d`);
|
||||
let b = pyodide.runPython(`d`);
|
||||
result.push(a.get(1));
|
||||
a.destroy();
|
||||
result.push(b.get(1));
|
||||
a.destroy();
|
||||
b.destroy();
|
||||
return result;
|
||||
"""
|
||||
)
|
||||
|
@ -613,6 +671,7 @@ def test_pyproxy_copy(selenium):
|
|||
assert result[1] == 2
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_errors(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
|
@ -652,6 +711,7 @@ def test_errors(selenium):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_fatal_error(selenium_standalone):
|
||||
"""Inject fatal errors in all the reasonable entrypoints"""
|
||||
selenium_standalone.run_js(
|
||||
|
@ -766,14 +826,18 @@ def test_pyproxy_call(selenium):
|
|||
msg = r"TypeError: f\(\) got multiple values for argument 'x'"
|
||||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js("f.callKwargs(76, {x : 6})")
|
||||
selenium.run_js("f.destroy()")
|
||||
|
||||
|
||||
def test_pyproxy_name_clash(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
let d = pyodide.runPython("{'a' : 2}");
|
||||
let d_get = d.$get;
|
||||
assert(() => d.get('a') === 2);
|
||||
assert(() => d.$get('b', 3) === 3);
|
||||
assert(() => d_get('b', 3) === 3);
|
||||
d_get.destroy();
|
||||
d.destroy();
|
||||
|
||||
let t = pyodide.runPython(`
|
||||
class Test:
|
||||
|
@ -781,7 +845,9 @@ def test_pyproxy_name_clash(selenium):
|
|||
return 7
|
||||
Test()
|
||||
`);
|
||||
assert(() => t.$destroy() === 7);
|
||||
let t_dest = t.$destroy;
|
||||
assert(() => t_dest() === 7);
|
||||
t_dest.destroy();
|
||||
t.destroy();
|
||||
assertThrows(() => t.$destroy, "Error", "Object has already been destroyed");
|
||||
"""
|
||||
|
|
|
@ -37,9 +37,13 @@ def test_import_js(selenium):
|
|||
|
||||
def test_globals_get_multiple(selenium):
|
||||
"""See #1151"""
|
||||
selenium.run("v = 0.123")
|
||||
selenium.run_js("pyodide.globals.get('v')")
|
||||
selenium.run_js("pyodide.globals.get('v')")
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("v = 0.123");
|
||||
pyodide.globals.get('v')
|
||||
pyodide.globals.get('v')
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_open_url(selenium, httpserver):
|
||||
|
@ -63,10 +67,10 @@ def test_load_package_after_convert_string(selenium):
|
|||
"""
|
||||
See #93.
|
||||
"""
|
||||
selenium.run("import sys\n" "x = sys.version")
|
||||
selenium.run_js("let x = pyodide.globals.get('x');\n" "console.log(x);")
|
||||
selenium.load_package("kiwisolver")
|
||||
selenium.run("import kiwisolver")
|
||||
selenium.run("import sys; x = sys.version")
|
||||
selenium.run_js("let x = pyodide.runPython('x'); console.log(x);")
|
||||
selenium.load_package("pytz")
|
||||
selenium.run("import pytz")
|
||||
|
||||
|
||||
def test_version_info(selenium):
|
||||
|
@ -131,15 +135,18 @@ def test_runpythonasync_exception_after_import(selenium_standalone):
|
|||
|
||||
|
||||
def test_py(selenium_standalone):
|
||||
selenium_standalone.run(
|
||||
selenium_standalone.run_js(
|
||||
"""
|
||||
def func():
|
||||
return 42
|
||||
pyodide.runPython(`
|
||||
def func():
|
||||
return 42
|
||||
`);
|
||||
let func = pyodide.globals.get('func');
|
||||
assert(() => func() === 42);
|
||||
func.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
assert selenium_standalone.run_js("return pyodide.globals.get('func')()") == 42
|
||||
|
||||
|
||||
def test_eval_nothing(selenium):
|
||||
assert selenium.run("# comment") is None
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# See also test_pyproxy, test_jsproxy, and test_python.
|
||||
import pytest
|
||||
from pyodide_build.testing import run_in_pyodide
|
||||
from hypothesis import given, settings, assume, strategies
|
||||
from hypothesis.strategies import text, from_type
|
||||
from conftest import selenium_context_manager
|
||||
|
@ -213,10 +214,14 @@ def test_python2js(selenium):
|
|||
assert selenium.run_js('return pyodide.runPython("\'碘化物\'") === "碘化物"')
|
||||
assert selenium.run_js('return pyodide.runPython("\'🐍\'") === "🐍"')
|
||||
assert selenium.run_js(
|
||||
"let x = pyodide.runPython(\"b'bytes'\").toJs();\n"
|
||||
"return (x instanceof window.Uint8Array) && "
|
||||
"(x.length === 5) && "
|
||||
"(x[0] === 98)"
|
||||
"""
|
||||
let xpy = pyodide.runPython("b'bytes'");
|
||||
let x = xpy.toJs();
|
||||
xpy.destroy();
|
||||
return (x instanceof window.Uint8Array) &&
|
||||
(x.length === 5) &&
|
||||
(x[0] === 98)
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
|
@ -234,13 +239,17 @@ def test_python2js(selenium):
|
|||
let typename = proxy.type;
|
||||
let x = proxy.toJs();
|
||||
proxy.destroy();
|
||||
return (typename === "dict") && (x.constructor.name === "Map") && (x.get(42) === 64)
|
||||
return (typename === "dict") && (x.constructor.name === "Map") && (x.get(42) === 64);
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let x = pyodide.runPython("open('/foo.txt', 'wb')")
|
||||
return (x.tell() === 0)
|
||||
let x_tell = x.tell;
|
||||
let result = x_tell();
|
||||
x.destroy();
|
||||
x_tell.destroy();
|
||||
return result === 0;
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -392,6 +401,7 @@ def test_js2python(selenium):
|
|||
assert selenium.run("bool(t.jsobject) == True")
|
||||
assert selenium.run("bool(t.jsarray0) == False")
|
||||
assert selenium.run("bool(t.jsarray1) == True")
|
||||
selenium.run_js("test_objects.jspython.destroy()")
|
||||
|
||||
|
||||
def test_js2python_bool(selenium):
|
||||
|
@ -467,37 +477,43 @@ def test_array_buffer(selenium):
|
|||
def assert_js_to_py_to_js(selenium, name):
|
||||
selenium.run_js(f"window.obj = {name};")
|
||||
selenium.run("from js import obj")
|
||||
assert selenium.run_js("return pyodide.globals.get('obj') === obj;")
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
let pyobj = pyodide.globals.get("obj");
|
||||
return pyobj === obj;
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def assert_py_to_js_to_py(selenium, name):
|
||||
selenium.run_js(f"window.obj = pyodide.globals.get('{name}');")
|
||||
assert selenium.run(
|
||||
selenium.run_js(
|
||||
f"""
|
||||
from js import obj
|
||||
obj is {name}
|
||||
window.obj = pyodide.runPython('{name}');
|
||||
pyodide.runPython(`
|
||||
from js import obj
|
||||
assert obj is {name}
|
||||
`);
|
||||
obj.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_recursive_list_to_js(selenium_standalone):
|
||||
selenium_standalone.run(
|
||||
"""
|
||||
x = []
|
||||
x.append(x)
|
||||
"""
|
||||
)
|
||||
selenium_standalone.run_js("x = pyodide.globals.get('x').toJs();")
|
||||
@run_in_pyodide
|
||||
def test_recursive_list_to_js():
|
||||
x = []
|
||||
x.append(x)
|
||||
from pyodide import to_js
|
||||
|
||||
to_js(x)
|
||||
|
||||
|
||||
def test_recursive_dict_to_js(selenium_standalone):
|
||||
selenium_standalone.run(
|
||||
"""
|
||||
x = {}
|
||||
x[0] = x
|
||||
"""
|
||||
)
|
||||
selenium_standalone.run_js("x = pyodide.globals.get('x').toJs();")
|
||||
@run_in_pyodide
|
||||
def test_recursive_dict_to_js():
|
||||
x = {}
|
||||
x[0] = x
|
||||
from pyodide import to_js
|
||||
|
||||
to_js(x)
|
||||
|
||||
|
||||
def test_list_js2py2js(selenium):
|
||||
|
@ -589,9 +605,9 @@ def test_javascript_error_back_to_js(selenium):
|
|||
)
|
||||
== "JsException"
|
||||
)
|
||||
assert selenium.run_js(
|
||||
selenium.run_js(
|
||||
"""
|
||||
return pyodide.globals.get("py_err") === err;
|
||||
assert(() => pyodide.globals.get("py_err") === err);
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -606,7 +622,7 @@ def test_memoryview_conversion(selenium):
|
|||
)
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.globals.get("a")
|
||||
pyodide.runPython("a").destroy()
|
||||
// Implicit assertion: this doesn't leave python error indicator set
|
||||
// (automatically checked in conftest.py)
|
||||
"""
|
||||
|
@ -614,7 +630,7 @@ def test_memoryview_conversion(selenium):
|
|||
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.globals.get("b")
|
||||
pyodide.runPython("b").destroy()
|
||||
// Implicit assertion: this doesn't leave python error indicator set
|
||||
// (automatically checked in conftest.py)
|
||||
"""
|
||||
|
@ -642,42 +658,54 @@ def test_python2js_with_depth(selenium):
|
|||
for(let i = 0; i < 4; i++){
|
||||
assert(() => proxies[i] == result_proxies[i]);
|
||||
}
|
||||
x.destroy();
|
||||
for(let px of proxies){
|
||||
px.destroy();
|
||||
}
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs(selenium):
|
||||
def test_tojs1(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("a = [1, 2, 3]");
|
||||
let res = pyodide.globals.get("a").toJs();
|
||||
let respy = pyodide.runPython("[1, 2, 3]");
|
||||
let res = respy.toJs();
|
||||
respy.destroy();
|
||||
return (Array.isArray(res)) && JSON.stringify(res) === "[1,2,3]";
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs2(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("a = (1, 2, 3)");
|
||||
let res = pyodide.globals.get("a").toJs();
|
||||
let respy = pyodide.runPython("(1, 2, 3)");
|
||||
let res = respy.toJs();
|
||||
respy.destroy();
|
||||
return (Array.isArray(res)) && JSON.stringify(res) === "[1,2,3]";
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs3(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("a = [(1,2), (3,4), [5, 6], { 2 : 3, 4 : 9}]")
|
||||
let res = pyodide.globals.get("a").toJs();
|
||||
let respy = pyodide.runPython("[(1,2), (3,4), [5, 6], { 2 : 3, 4 : 9}]")
|
||||
let res = respy.toJs();
|
||||
respy.destroy();
|
||||
return Array.isArray(res) && \
|
||||
JSON.stringify(res) === `[[1,2],[3,4],[5,6],{}]` && \
|
||||
JSON.stringify(Array.from(res[3].entries())) === "[[2,3],[4,9]]";
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs4(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("a = [1,[2,[3,[4,[5,[6,[7]]]]]]]")
|
||||
let a = pyodide.globals.get("a");
|
||||
let a = pyodide.runPython("[1,[2,[3,[4,[5,[6,[7]]]]]]]")
|
||||
for(let i=0; i < 7; i++){
|
||||
let x = a.toJs(i);
|
||||
for(let j=0; j < i; j++){
|
||||
|
@ -685,14 +713,17 @@ def test_tojs(selenium):
|
|||
x = x[1];
|
||||
}
|
||||
assert(() => pyodide.isPyProxy(x), `i: ${i}, j: ${i}`);
|
||||
x.destroy();
|
||||
}
|
||||
a.destroy()
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs5(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython("a = [1, (2, (3, [4, (5, (6, [7]))]))]")
|
||||
let a = pyodide.globals.get("a");
|
||||
let a = pyodide.runPython("[1, (2, (3, [4, (5, (6, [7]))]))]")
|
||||
for(let i=0; i < 7; i++){
|
||||
let x = a.toJs(i);
|
||||
for(let j=0; j < i; j++){
|
||||
|
@ -700,20 +731,25 @@ def test_tojs(selenium):
|
|||
x = x[1];
|
||||
}
|
||||
assert(() => pyodide.isPyProxy(x), `i: ${i}, j: ${i}`);
|
||||
x.destroy();
|
||||
}
|
||||
a.destroy()
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs6(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython(`
|
||||
let respy = pyodide.runPython(`
|
||||
a = [1, 2, 3, 4, 5]
|
||||
b = [a, a, a, a, a]
|
||||
c = [b, b, b, b, b]
|
||||
[b, b, b, b, b]
|
||||
`);
|
||||
let total_refs = pyodide._module.hiwire.num_keys();
|
||||
let res = pyodide.globals.get("c").toJs();
|
||||
let res = respy.toJs();
|
||||
let new_total_refs = pyodide._module.hiwire.num_keys();
|
||||
respy.destroy();
|
||||
assert(() => total_refs === new_total_refs);
|
||||
assert(() => res[0] === res[1]);
|
||||
assert(() => res[0][0] === res[1][1]);
|
||||
|
@ -721,17 +757,21 @@ def test_tojs(selenium):
|
|||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs7(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython(`
|
||||
let respy = pyodide.runPython(`
|
||||
a = [["b"]]
|
||||
b = [1,2,3, a[0]]
|
||||
a[0].append(b)
|
||||
a.append(b)
|
||||
a
|
||||
`);
|
||||
let total_refs = pyodide._module.hiwire.num_keys();
|
||||
let res = pyodide.globals.get("a").toJs();
|
||||
let res = respy.toJs();
|
||||
let new_total_refs = pyodide._module.hiwire.num_keys();
|
||||
respy.destroy();
|
||||
assert(() => total_refs === new_total_refs);
|
||||
assert(() => res[0][0] === "b");
|
||||
assert(() => res[1][2] === 3);
|
||||
|
@ -739,13 +779,18 @@ def test_tojs(selenium):
|
|||
assert(() => res[0][1] === res[1]);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_tojs8(selenium):
|
||||
msg = "pyodide.ConversionError"
|
||||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython(`
|
||||
{ (2,2) : 0 }
|
||||
`).toJs()
|
||||
from pyodide import to_js
|
||||
to_js({ (2,2) : 0 })
|
||||
`);
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -753,18 +798,22 @@ def test_tojs(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
pyodide.runPython(`
|
||||
{ (2,2) }
|
||||
`).toJs()
|
||||
from pyodide import to_js
|
||||
to_js({ (2,2) })
|
||||
`);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_tojs9(selenium):
|
||||
assert (
|
||||
set(
|
||||
selenium.run_js(
|
||||
"""
|
||||
return Array.from(pyodide.runPython(`
|
||||
{ 1, "1" }
|
||||
`).toJs().values())
|
||||
from pyodide import to_js
|
||||
to_js({ 1, "1" })
|
||||
`).values())
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
@ -776,8 +825,9 @@ def test_tojs(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
return Array.from(pyodide.runPython(`
|
||||
{ 1 : 7, "1" : 9 }
|
||||
`).toJs().entries())
|
||||
from pyodide import to_js
|
||||
to_js({ 1 : 7, "1" : 9 })
|
||||
`).entries())
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import pytest
|
||||
|
||||
|
||||
def run_with_resolve(selenium, code):
|
||||
selenium.run_js(
|
||||
f"""
|
||||
|
@ -25,6 +28,7 @@ def test_asyncio_sleep(selenium):
|
|||
print('sleeping done')
|
||||
resolve()
|
||||
asyncio.ensure_future(sleep_task())
|
||||
None
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -47,6 +51,7 @@ def test_return_result(selenium):
|
|||
import asyncio
|
||||
fut = asyncio.ensure_future(foo(998))
|
||||
fut.add_done_callback(check_result)
|
||||
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -85,6 +90,7 @@ def test_await_js_promise(selenium):
|
|||
resolve()
|
||||
import asyncio
|
||||
asyncio.ensure_future(fetch_task())
|
||||
None
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -101,6 +107,7 @@ def test_call_soon(selenium):
|
|||
raise Exception("Expected arg == 'bar'...")
|
||||
import asyncio
|
||||
asyncio.get_event_loop().call_soon(foo, 'bar')
|
||||
None
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -122,6 +129,7 @@ def test_contextvars(selenium):
|
|||
raise Exception(f"Expected request_id.get() == '123', got {request_id.get()!r}")
|
||||
import asyncio
|
||||
asyncio.get_event_loop().call_soon(func_ctx, context=ctx)
|
||||
None
|
||||
""",
|
||||
)
|
||||
|
||||
|
@ -141,10 +149,12 @@ def test_asyncio_exception(selenium):
|
|||
resolve()
|
||||
import asyncio
|
||||
asyncio.ensure_future(capture_exception())
|
||||
None
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_run_in_executor(selenium):
|
||||
# If run_in_executor tries to actually use ThreadPoolExecutor, it will throw
|
||||
# an error since we can't start threads
|
||||
|
|
Loading…
Reference in New Issue