From 55fbd32ef319c2a2ed5ce297cf4b863a171ac8c4 Mon Sep 17 00:00:00 2001 From: Gyeongjae Choi Date: Tue, 15 Feb 2022 20:20:24 +0900 Subject: [PATCH] ENH Show informative error message when fetching wheel fails (#2175) Co-authored-by: Roman Yurchak --- docs/usage/loading-packages.md | 9 +++++++-- packages/micropip/src/micropip/_micropip.py | 15 ++++++++++++++- packages/micropip/test_micropip.py | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/usage/loading-packages.md b/docs/usage/loading-packages.md index aeec685fa..73c733137 100644 --- a/docs/usage/loading-packages.md +++ b/docs/usage/loading-packages.md @@ -113,11 +113,16 @@ be the case if the wheels is made using standard Python tools (`pip wheel`, All required dependencies must have been previously installed with {mod}`micropip` or {any}`pyodide.loadPackage`. -If the file is on a remote server, the server must set Cross-Origin Resource Sharing -(CORS) headers to allow access. Otherwise, you can prepend a CORS proxy to the +```{admonition} Cross-Origin Resource Sharing (CORS) +:class: info + +If the file is on a remote server, the server must set +[Cross-Origin Resource Sharing (CORS) headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) +to allow access. Otherwise, you can prepend a CORS proxy to the URL. Note however that using third-party CORS proxies has security implications, particularly since we are not able to check the file integrity, unlike with installs from PyPI. +``` ## Example diff --git a/packages/micropip/src/micropip/_micropip.py b/packages/micropip/src/micropip/_micropip.py index 236433f3e..38fbbb856 100644 --- a/packages/micropip/src/micropip/_micropip.py +++ b/packages/micropip/src/micropip/_micropip.py @@ -295,7 +295,20 @@ class _PackageManager: async def add_wheel(self, name, wheel, version, extras, ctx, transaction): transaction["locked"][name] = PackageMetadata(name=name, version=version) - wheel_bytes = await fetch_bytes(wheel["url"]) + + try: + wheel_bytes = await fetch_bytes(wheel["url"]) + except Exception as e: + if wheel["url"].startswith("https://files.pythonhosted.org/"): + raise e + else: + raise ValueError( + f"Couldn't fetch wheel from '{wheel['url']}'." + "One common reason for this is when the server blocks " + "Cross-Origin Resource Sharing (CORS)." + "Check if the server is sending the correct 'Access-Control-Allow-Origin' header." + ) from e + wheel["wheel_bytes"] = wheel_bytes with ZipFile(io.BytesIO(wheel_bytes)) as zip_file: # type: ignore diff --git a/packages/micropip/test_micropip.py b/packages/micropip/test_micropip.py index 6dd8da4e5..c8e8daeb3 100644 --- a/packages/micropip/test_micropip.py +++ b/packages/micropip/test_micropip.py @@ -345,6 +345,22 @@ def test_install_keep_going(monkeypatch): ) +def test_fetch_wheel_fail(monkeypatch): + pytest.importorskip("packaging") + from micropip import _micropip + + def _mock_fetch_bytes(*args, **kwargs): + raise Exception("Failed to fetch") + + monkeypatch.setattr(_micropip, "fetch_bytes", _mock_fetch_bytes) + + msg = "Access-Control-Allow-Origin" + with pytest.raises(ValueError, match=msg): + asyncio.get_event_loop().run_until_complete( + _micropip.install("htps://x.com/xxx-1.0.0-py3-none-any.whl") + ) + + def test_list_pypi_package(monkeypatch): pytest.importorskip("packaging") from micropip import _micropip