def test_to_js_default_converter2(selenium): import json import pytest from js import JSON, Array from pyodide import JsException, run_js, to_js class Pair: def __init__(self, first, second): self.first = first self.second = second p1 = Pair(1, 2) p2 = Pair(1, 2) p2.first = p2 def default_converter(value, convert, cacheConversion): result = Array.new() cacheConversion(value, result) result.push(convert(value.first)) result.push(convert(value.second)) return result p1js = to_js(p1, default_converter=default_converter) p2js = to_js(p2, default_converter=default_converter) assert json.loads(JSON.stringify(p1js)) == [1, 2] with pytest.raises(JsException, match="TypeError"): JSON.stringify(p2js) assert run_js("(x) => x[0] === x")(p2js) assert run_js("(x) => x[1] === 2")(p2js)
def test_javascript_error_back_to_js(selenium): from pyodide import run_js err = run_js('self.err = new Error("This is a js error"); err') assert type(err).__name__ == "JsException" assert run_js(""" (py_err) => py_err === err; """)(err)
def test_tojs1(selenium, ty): import json from pyodide import run_js l = [1, 2, 3] x = ty(l) assert run_js("x => Array.isArray(x.toJs())")(x) serialized = run_js("x => JSON.stringify(x.toJs())")(x) assert l == json.loads(serialized)
def test_python2js_long_ints2(selenium): from pyodide import run_js assert run_js(""" (x) => x === 2n**64n; """)(2**64) assert run_js(""" (x) => x === -(2n**64n); """)(-(2**64))
def main(selenium, sbytes): from pyodide import run_js spy = bytes(sbytes).decode() sjs = run_js(""" (sbytes) => { self.sjs = (new TextDecoder("utf8")).decode(new Uint8Array(sbytes)); return sjs; } """)(sbytes) assert sjs == spy assert run_js("(spy) => spy === self.sjs")(spy)
def test_tojs2(selenium): import json from pyodide import run_js o = [(1, 2), (3, 4), [5, 6], {2: 3, 4: 9}] assert run_js("(o) => Array.isArray(o.toJs())")(o) serialized = run_js("(o) => JSON.stringify(o.toJs())")(o) assert json.loads(serialized) == [[1, 2], [3, 4], [5, 6], {}] serialized = run_js( "(o) => JSON.stringify(Array.from(o.toJs()[3].entries()))")(o) assert json.loads(serialized) == [[2, 3], [4, 9]]
def test_nan_conversions(selenium): from pyodide import run_js jsnan = run_js("NaN") from math import isnan assert isnan(jsnan) assert run_js(""" let mathmod = pyodide.pyimport("math"); const res = Number.isNaN(mathmod.nan); mathmod.destroy(); res """)
def test_large_string_conversion(selenium): from pyodide import run_js longstr = run_js('"ab".repeat(200_000)') res = longstr.count("ab") assert res == 200_000 run_js(""" (s) => { assert(() => s.length === 40_000); for(let n = 0; n < 20_000; n++){ assert(() => s.slice(2*n, 2*n+2) === "ab"); } } """)("ab" * 20_000)
def test_string_conversion2(selenium, s): from pyodide import run_js s_encoded = s.encode() sjs = run_js(""" (s_encoded) => { let buf = s_encoded.getBuffer(); self.sjs = (new TextDecoder("utf8")).decode(buf.data); buf.release(); return sjs } """)(s_encoded) assert sjs == s assert run_js("""(spy) => spy === self.sjs""")(s)
def test_hyp_tojs_no_crash(selenium, obj): import __main__ from pyodide import run_js __main__.x = obj try: run_js(""" let x = pyodide.globals.get("x"); if(x && x.toJs){ x.toJs(); } """) finally: del __main__.x
def test_dict_converter1(selenium): import json from pyodide import run_js, to_js arrayFrom = run_js("Array.from") d = {x: x + 2 for x in range(5)} res = to_js(d, dict_converter=arrayFrom) constructor, serialized = run_js(""" (res) => { return [res.constructor.name, JSON.stringify(res)]; } """)(res) assert constructor == "Array" assert json.loads(serialized) == [list(x) for x in d.items()]
def test_to_py_default_converter2(selenium): from typing import Any from pyodide import run_js [p1, p2] = run_js(""" class Pair { constructor(first, second){ this.first = first; this.second = second; } } const l = [1,2,3]; const r1 = new Pair(l, [l]); const r2 = new Pair(l, [l]); r2.first = r2; [r1, r2] """) def default_converter(value, converter, cache): if value.constructor.name != "Pair": return value l: list[Any] = [] cache(value, l) l.append(converter(value.first)) l.append(converter(value.second)) return l r1 = p1.to_py(default_converter=default_converter) assert isinstance(r1, list) assert r1[0] is r1[1][0] assert r1[0] == [1, 2, 3] r2 = p2.to_py(default_converter=default_converter) assert r2[0] is r2
def test_object_with_null_constructor(selenium): from unittest import TestCase from pyodide import run_js o = run_js("Object.create(null)") with TestCase().assertRaises(TypeError): repr(o)
def test_to_py4(selenium, obj, msg): import pytest from pyodide import ConversionError, JsException, run_js a = run_js(f"new {obj}") with pytest.raises((ConversionError, JsException), match=msg): a.to_py()
def test_python2js5(selenium): from pyodide import run_js assert run_js("(x) => x.tell()")(open("/foo.txt", "wb")) == 0 from tempfile import TemporaryFile with TemporaryFile(mode="w+") as f: contents = ["a\n", "b\n", "hello there!\n"] f.writelines(contents) assert run_js("(f) => f.tell()")(f) == 17 assert (run_js(""" (f) => { f.seek(0); return [f.readline(), f.readline(), f.readline()]; } """)(f).to_py() == contents)
def test_negative_length(selenium, n): from unittest import TestCase from pyodide import run_js raises = TestCase().assertRaises(ValueError, msg=f"length {n} of object is negative") o = run_js(f"({{length : {n}}})") with raises: len(o) # 1. Set toStringTag to NodeList to force JsProxy to feature detect this object # as an array # 2. Return a negative length # 3. JsProxy_subscript_array should successfully handle this and propagate the error. a = run_js(f"({{[Symbol.toStringTag] : 'NodeList', length: {n}}})") with raises: a[-1]
def test_wrong_way_conversions2(selenium): from pyodide import run_js, to_js [astr, bstr] = run_js(""" (a) => { b = [1,2,3]; return [JSON.stringify(a), JSON.stringify(b)] } """)(to_js([1, 2, 3])) assert astr == bstr
def test_hyp_py2js2py_2(selenium, obj): import __main__ from pyodide import run_js __main__.o = obj try: assert obj == run_js("pyodide.globals.get('o')") finally: del __main__.o
def test_typed_arrays(selenium, jstype, pytype): from pyodide import run_js array = run_js(f"new {jstype}([1, 2, 3, 4]);").to_py() print(array.format, array.tolist(), array.tobytes()) assert array.format == pytype assert array.tolist() == [1, 2, 3, 4] import struct assert array.tobytes() == struct.pack(pytype * 4, 1, 2, 3, 4)
def test_python2js2(selenium): from pyodide import run_js assert (list( run_js(""" (x) => { x = x.toJs(); return [x.constructor.name, x.length, x[0]]; } """)(b"bytes")) == ["Uint8Array", 5, 98])
def test_hyp_py2js2py(selenium, obj): import __main__ from pyodide import run_js __main__.obj = obj try: run_js('self.obj2 = pyodide.globals.get("obj"); 0;') from js import obj2 assert obj2 == obj run_js(""" if(self.obj2 && self.obj2.destroy){ self.obj2.destroy(); } delete self.obj2 """) finally: del __main__.obj
def test_python2js3(selenium): from pyodide import run_js l = [7, 9, 13] result = run_js(""" (proxy) => { x = proxy.toJs(); return [proxy.type, x.constructor.name, x.length, x[0], x[1], x[2]] } """)(l) assert list(result) == ["list", "Array", 3, *l]
def test_big_integer_py2js2py(selenium, a): import __main__ from pyodide import run_js __main__.a = a try: b = run_js("pyodide.globals.get('a')") assert a == b finally: del __main__.a
def test_wrong_way_conversions3(selenium): from pyodide import run_js, to_js class Test: pass t1 = Test() t2 = to_js(t1) t3 = run_js("(t2) => t2.copy()")(t2) assert t1 is t3 t2.destroy()
async def test_remove_event_listener_twice(selenium): from pyodide import run_js x = run_js(""" class MockObject { constructor() { this.listeners = {}; } addEventListener(event, handler) { if (event in this.listeners) { this.listeners[event].push(handler); } else { this.listeners[event] = [handler]; } } removeEventListener(event, handler) { if (event in this.listeners) { this.listeners[event] = this.listeners[event].filter( (existingHandler) => existingHandler !== handler ) } } triggerEvent(event) { if (this.listeners[event]) { for (const handler of this.listeners[event]) { handler({}); } } } } let x = new MockObject(); x; """) triggered = False error_raised = False def foo(obj): nonlocal triggered triggered = True from pyodide import add_event_listener, remove_event_listener add_event_listener(x, "click", foo) remove_event_listener(x, "click", foo) try: remove_event_listener(x, "click", foo) except KeyError: error_raised = True assert error_raised
def test_to_py3(selenium): from pyodide import run_js a = run_js(""" class Temp { constructor(){ this.x = 2; this.y = 7; } } new Temp(); """) assert repr(type(a.to_py())) == "<class 'pyodide.JsProxy'>"
def main(selenium, s): import json from pyodide import run_js x_py = json.loads(s) run_js(f""" self.x_js = eval('{s}n'); // JSON.parse apparently doesn't work """) [x1, x2] = run_js(""" (x_py) => [x_py.toString(), x_js.toString()] """)(x_py) assert x1 == x2 from js import x_js check = run_js(""" (x) => { const [a, b] = x.toJs(); return a === b; } """)([str(x_js), str(x_py)]) assert check
def test_python2js4(selenium): from pyodide import run_js assert (list( run_js(""" (proxy) => { let typename = proxy.type; let x = proxy.toJs(); return [proxy.type, x.constructor.name, x.get(42)]; } """)({ 42: 64 })) == ["dict", "Map", 64])
def test_dict_converter3(selenium): import json from js import Object from pyodide import run_js, to_js d = {x: x + 2 for x in range(5)} res = to_js(d, dict_converter=Object.fromEntries) constructor, serialized = run_js(""" (res) => [res.constructor.name, JSON.stringify(res)] """)(res) assert constructor == "Object" assert json.loads(serialized) == {str(k): v for k, v in d.items()}
def test_jsproxy_attribute_error(selenium): import pytest from pyodide import run_js point = run_js(""" class Point { constructor(x, y) { this.x = x; this.y = y; } } new Point(42, 43); """) assert point.y == 43 with pytest.raises(AttributeError, match="z"): point.z del point.y with pytest.raises(AttributeError, match="y"): point.y assert run_js("(point) => point.y;")(point) is None