Skip to content

Commit b1b1b17

Browse files
authored
Make sure load_package() fails when a url returns a 404 (#184)
1 parent 9f29aa4 commit b1b1b17

2 files changed

Lines changed: 87 additions & 1 deletion

File tree

pytest_pyodide/runner.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,39 @@ def run_webworker(self, code):
316316
)
317317

318318
def load_package(self, packages):
319-
self.run_js(f"await pyodide.loadPackage({packages!r})")
319+
# Pyodide's ``loadPackage`` reports failures in two different ways:
320+
#
321+
# * For a known-but-unresolvable package (e.g. a wheel URL that
322+
# 404s), the returned promise resolves normally and the failure
323+
# is only delivered via the ``errorCallback`` option.
324+
# * For an unknown package name, the returned promise rejects
325+
# with a JavaScript ``Error``.
326+
#
327+
# Both paths are easy to miss in tests -- in the first case the
328+
# missing package surfaces later as a confusing
329+
# ``ModuleNotFoundError`` inside Pyodide. We normalize both into a
330+
# single ``RuntimeError`` raised at the call site so load failures
331+
# are always reported immediately and with the problematic package
332+
# reference in the message.
333+
result = self.run_js(
334+
f"""
335+
const __errors = [];
336+
try {{
337+
await pyodide.loadPackage({packages!r}, {{
338+
errorCallback: (msg) => {{ __errors.push(msg); }},
339+
}});
340+
}} catch (e) {{
341+
__errors.push(e.message || String(e));
342+
}}
343+
return __errors;
344+
"""
345+
)
346+
if result:
347+
raise RuntimeError(
348+
"pyodide.loadPackage({!r}) reported errors:\n {}".format(
349+
packages, "\n ".join(result)
350+
)
351+
)
320352

321353

322354
class _SeleniumBaseRunner(_BrowserBaseRunner):

tests/test_load_package.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Tests for ``SeleniumBrowserRunner.load_package``.
2+
3+
Pyodide's ``pyodide.loadPackage`` does not raise when a package cannot be
4+
loaded (e.g. the wheel URL 404s or the package name is unknown); it only
5+
invokes the ``errorCallback`` option. Our ``load_package`` wrapper collects
6+
those callback messages and turns them into a ``RuntimeError`` so that the
7+
failure is reported at the call site rather than surfacing later as a
8+
confusing ``ModuleNotFoundError`` inside Pyodide.
9+
"""
10+
11+
import pytest
12+
13+
14+
def test_load_package_succeeds(selenium):
15+
"""Sanity check: loading a package that exists in the Pyodide dist
16+
must not raise, and the package must be importable afterwards."""
17+
selenium.load_package("micropip")
18+
# If the package is really loaded, importing it in Pyodide succeeds.
19+
selenium.run_js("await pyodide.runPythonAsync('import micropip');")
20+
21+
22+
def test_load_package_bad_url_raises(selenium):
23+
"""A URL that 404s must cause load_package to raise, and the error
24+
message must mention the failure reported by Pyodide."""
25+
bad_url = (
26+
f"http://{selenium.server_hostname}:{selenium.server_port}"
27+
"/does-not-exist-pytest_pyodide_test.whl"
28+
)
29+
with pytest.raises(RuntimeError) as exc_info:
30+
selenium.load_package(bad_url)
31+
32+
msg = str(exc_info.value)
33+
assert "loadPackage" in msg
34+
assert bad_url in msg
35+
36+
37+
def test_load_package_unknown_name_raises(selenium):
38+
"""An unknown package name must also cause load_package to raise."""
39+
with pytest.raises(RuntimeError) as exc_info:
40+
selenium.load_package("definitely-not-a-real-package-xyz")
41+
42+
msg = str(exc_info.value)
43+
assert "loadPackage" in msg
44+
assert "definitely-not-a-real-package-xyz" in msg
45+
46+
47+
def test_load_package_partial_failure_raises(selenium):
48+
"""If a list contains both a valid and an invalid package, the call
49+
must still raise so the failure is not silently swallowed."""
50+
with pytest.raises(RuntimeError) as exc_info:
51+
selenium.load_package(["micropip", "definitely-not-a-real-package-xyz"])
52+
53+
msg = str(exc_info.value)
54+
assert "definitely-not-a-real-package-xyz" in msg

0 commit comments

Comments
 (0)