From 4124f18d1a182d0be222e0a42c7aa7ccc3726498 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 10 Oct 2018 14:17:29 -0400 Subject: [PATCH] Fix package name / import name dichotomy. Add testing. --- src/runpython.c | 33 ++++++++++++++------- test/conftest.py | 29 +++++++++++++++++++ test/test_python.py | 70 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/runpython.c b/src/runpython.c index 693f85c7d..ce086db41 100644 --- a/src/runpython.c +++ b/src/runpython.c @@ -77,20 +77,31 @@ EM_JS(int, runpython_init_js, (), { var jsimports = Module.hiwire_get_value(idimports); Module.hiwire_decref(idimports); - var internal = function() { return Module._runPythonInternal(pycode); }; + var internal = function(resolve, reject) { + try { + resolve(Module._runPythonInternal(pycode)); + } catch (e) { + reject(e); + } + }; if (jsimports.length) { - var packages = window.pyodide._module.packages.dependencies; - var packageFilter = function(name) - { - return Object.prototype.hasOwnProperty(packages, name); - }; - jsimports = jsimports.filter(packageFilter); - return Module.loadPackage(jsimports, messageCallback).then(internal); - } else { - var resolve = function(resolve) { return resolve(); }; - return new Promise(resolve).then(internal); + var packageNames = window.pyodide._module.packages.import_name_to_package_name; + var packages = {}; + for (var i = 0; i < jsimports.length; ++i) { + var name = jsimports[i]; + if (packageNames[name] !== undefined) { + packages[packageNames[name]] = undefined; + } + } + if (Object.keys(packages).length) { + var runInternal = function() { + return new Promise(internal); + }; + return Module.loadPackage(Object.keys(packages), messageCallback).then(runInternal); + } } + return new Promise(internal); }; }); diff --git a/test/conftest.py b/test/conftest.py index 02096475a..1c926d38e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -105,6 +105,35 @@ class SeleniumWrapper: return self.run_js( 'return pyodide.runPython({!r})'.format(code)) + def run_async(self, code): + from selenium.common.exceptions import TimeoutException + if isinstance(code, str) and code.startswith('\n'): + # we have a multiline string, fix indentation + code = textwrap.dedent(code) + self.run_js( + """ + window.done = false; + pyodide.runPythonAsync({!r}) + .then(function(output) {{ window.output = output; window.error = false; }}, + function(output) {{ window.output = output; window.error = true; }}) + .finally(() => window.done = true); + """.format(code) + ) + try: + self.wait.until(PackageLoaded()) + except TimeoutException as exc: + _display_driver_logs(self.browser, self.driver) + print(self.logs) + raise TimeoutException('runPythonAsync timed out') + return self.run_js( + """ + if (window.error) { + throw window.output; + } + return window.output; + """ + ) + def run_js(self, code): if isinstance(code, str) and code.startswith('\n'): # we have a multiline string, fix indentation diff --git a/test/test_python.py b/test/test_python.py index 83ef16f2c..37783b36d 100644 --- a/test/test_python.py +++ b/test/test_python.py @@ -442,3 +442,73 @@ def test_recursive_dict(selenium_standalone): """ ) selenium_standalone.run_js("x = pyodide.pyimport('x')") + + +def test_runpythonasync(selenium_standalone): + output = selenium_standalone.run_async( + """ + import numpy as np + np.zeros(5) + """ + ) + assert list(output) == [0, 0, 0, 0, 0] + + +def test_runpythonasync_different_package_name(selenium_standalone): + output = selenium_standalone.run_async( + """ + import dateutil + dateutil.__version__ + """ + ) + assert isinstance(output, str) + + +def test_runpythonasync_no_imports(selenium_standalone): + output = selenium_standalone.run_async( + """ + 42 + """ + ) + assert output == 42 + + +def test_runpythonasync_missing_import(selenium_standalone): + try: + selenium_standalone.run_async( + """ + import foo + """ + ) + except selenium_standalone.JavascriptException as e: + assert "ModuleNotFoundError" in str(e) + else: + assert False + + +def test_runpythonasync_exception(selenium_standalone): + try: + selenium_standalone.run_async( + """ + 42 / 0 + """ + ) + except selenium_standalone.JavascriptException as e: + assert "ZeroDivisionError" in str(e) + else: + assert False + + +def test_runpythonasync_exception_after_import(selenium_standalone): + try: + selenium_standalone.run_async( + """ + import numpy as np + x = np.empty(5) + 42 / 0 + """ + ) + except selenium_standalone.JavascriptException as e: + assert "ZeroDivisionError" in str(e) + else: + assert False