import os from pathlib import Path import time import pytest def test_init(selenium_standalone): assert ('Python initialization complete' in selenium_standalone.logs.splitlines()) assert len(selenium_standalone.driver.window_handles) == 1 def test_webbrowser(selenium): selenium.run("import antigravity") time.sleep(2) assert len(selenium.driver.window_handles) == 2 def test_print(selenium): selenium.run("print('This should be logged')") assert 'This should be logged' in selenium.logs.splitlines() def test_python2js(selenium): assert selenium.run_js('return pyodide.runPython("None") === undefined') assert selenium.run_js('return pyodide.runPython("True") === true') assert selenium.run_js('return pyodide.runPython("False") === false') assert selenium.run_js('return pyodide.runPython("42") === 42') assert selenium.run_js('return pyodide.runPython("3.14") === 3.14') # Need to test all three internal string representations in Python: UCS1, # UCS2 and UCS4 assert selenium.run_js( 'return pyodide.runPython("\'ascii\'") === "ascii"') assert selenium.run_js( 'return pyodide.runPython("\'ιωδιούχο\'") === "ιωδιούχο"') assert selenium.run_js( 'return pyodide.runPython("\'碘化物\'") === "碘化物"') assert selenium.run_js( 'let x = pyodide.runPython("b\'bytes\'");\n' 'return (x instanceof window.Uint8ClampedArray) && ' '(x.length === 5) && ' '(x[0] === 98)') assert selenium.run_js( """ let x = pyodide.runPython("[1, 2, 3]"); return ((x instanceof window.Array) && (x.length === 3) && (x[0] == 1) && (x[1] == 2) && (x[2] == 3)) """) assert selenium.run_js( """ let x = pyodide.runPython("{42: 64}"); return (typeof x === "object") && (x[42] === 64) """) assert selenium.run_js( """ let x = pyodide.runPython("open('/foo.txt', 'wb')") return (x.tell() === 0) """) def test_pythonexc2js(selenium): try: selenium.run_js('return pyodide.runPython("5 / 0")') except selenium.JavascriptException as e: assert('ZeroDivisionError' in str(e)) else: assert False, 'Expected exception' def test_js2python(selenium): selenium.run_js( """ window.jsstring = "碘化物"; window.jsnumber0 = 42; window.jsnumber1 = 42.5; window.jsundefined = undefined; window.jsnull = null; window.jstrue = true; window.jsfalse = false; window.jspython = pyodide.pyimport("open"); window.jsbytes = new Uint8Array([1, 2, 3]); window.jsfloats = new Float32Array([1, 2, 3]); window.jsobject = new XMLHttpRequest(); """ ) assert selenium.run( 'from js import jsstring\n' 'jsstring == "碘化物"') assert selenium.run( 'from js import jsnumber0\n' 'jsnumber0 == 42') assert selenium.run( 'from js import jsnumber1\n' 'jsnumber1 == 42.5') assert selenium.run( 'from js import jsundefined\n' 'jsundefined is None') assert selenium.run( 'from js import jstrue\n' 'jstrue is True') assert selenium.run( 'from js import jsfalse\n' 'jsfalse is False') assert selenium.run( 'from js import jspython\n' 'jspython is open') assert selenium.run( """ from js import jsbytes ((jsbytes.tolist() == [1, 2, 3]) and (jsbytes.tobytes() == b"\x01\x02\x03")) """) assert selenium.run( """ from js import jsfloats import struct expected = struct.pack("fff", 1, 2, 3) (jsfloats.tolist() == [1, 2, 3]) and (jsfloats.tobytes() == expected) """) assert selenium.run( 'from js import jsobject\n' 'str(jsobject) == "[object XMLHttpRequest]"') @pytest.mark.parametrize('wasm_heap', (False, True)) @pytest.mark.parametrize( 'jstype, pytype', ( ('Int8Array', 'b'), ('Uint8Array', 'B'), ('Uint8ClampedArray', 'B'), ('Int16Array', 'h'), ('Uint16Array', 'H'), ('Int32Array', 'i'), ('Uint32Array', 'I'), ('Float32Array', 'f'), ('Float64Array', 'd'))) def test_typed_arrays(selenium, wasm_heap, jstype, pytype): if not wasm_heap: selenium.run_js( f'window.array = new {jstype}([1, 2, 3, 4]);\n') else: selenium.run_js( f""" var buffer = pyodide._module._malloc( 4 * {jstype}.BYTES_PER_ELEMENT); window.array = new {jstype}( pyodide._module.HEAPU8.buffer, buffer, 4); window.array[0] = 1; window.array[1] = 2; window.array[2] = 3; window.array[3] = 4; """) assert selenium.run( f""" from js import array import struct expected = struct.pack("{pytype*4}", 1, 2, 3, 4) print(array.format, array.tolist(), array.tobytes()) ((array.format == "{pytype}") and array.tolist() == [1, 2, 3, 4] and array.tobytes() == expected and array.obj._has_bytes() is {not wasm_heap}) """) def test_import_js(selenium): result = selenium.run( """ from js import window window.title = 'Foo' window.title """) assert result == 'Foo' def test_pyproxy(selenium): selenium.run( """ class Foo: bar = 42 def get_value(self, value): return value * 64 f = Foo() """ ) assert selenium.run_js("return pyodide.pyimport('f').get_value(2)") == 128 assert selenium.run_js("return pyodide.pyimport('f').bar") == 42 assert selenium.run_js("return ('bar' in pyodide.pyimport('f'))") selenium.run_js("f = pyodide.pyimport('f'); f.baz = 32") assert selenium.run("f.baz") == 32 assert set(selenium.run_js( "return Object.getOwnPropertyNames(pyodide.pyimport('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', 'toString', 'prototype', 'arguments', 'caller']) assert selenium.run("hasattr(f, 'baz')") selenium.run_js("delete pyodide.pyimport('f').baz") assert not selenium.run("hasattr(f, 'baz')") assert selenium.run_js( "return pyodide.pyimport('f').toString()").startswith(' LooseVersion('0.0.1') version_js_str = selenium.run_js("return pyodide.version()") version_js = LooseVersion(version_js_str) assert version_py == version_js def test_recursive_list(selenium_standalone): selenium_standalone.run( """ x = [] x.append(x) """ ) selenium_standalone.run_js("x = pyodide.pyimport('x')") def test_recursive_dict(selenium_standalone): selenium_standalone.run( """ x = {} x[0] = x """ ) selenium_standalone.run_js("x = pyodide.pyimport('x')")