Skip to content

Commit e6635a0

Browse files
committed
fetch_string_and_headers compat: raise in and out of pyodide
Currently only the not_in_pyodide will raise on non-success, because this is the default behavior of urllib, the in_pyodide will not, so I added a raise_for_status. It is better to raise, as otherwise the package parser will potentially get proper URL and not manage to parse it, and decide there is no wheels, while we actually just got an error (404, or maybe 500). In addition wraps both case in a custom local HttpStatusError, so that we can actually catch these errors in the right places when we encounter them. I think we could define HttpStatusError on the ABC and not make it an ABC itself, but it small enough that I think duplication is ok.
1 parent 8807c59 commit e6635a0

4 files changed

Lines changed: 41 additions & 3 deletions

File tree

micropip/_compat/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
to_js = compatibility_layer.to_js
3636

37+
HttpStatusError = compatibility_layer.HttpStatusError
38+
3739

3840
__all__ = [
3941
"REPODATA_INFO",
@@ -45,4 +47,5 @@
4547
"loadPackage",
4648
"get_dynlibs",
4749
"to_js",
50+
"HttpStatusError",
4851
]

micropip/_compat/_compat_in_pyodide.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from pyodide._package_loader import get_dynlibs
99
from pyodide.ffi import IN_BROWSER, to_js
10-
from pyodide.http import pyfetch
10+
from pyodide.http import HttpStatusError, pyfetch
1111

1212
from .compatibility_layer import CompatibilityLayer
1313

@@ -28,6 +28,15 @@
2828

2929

3030
class CompatibilityInPyodide(CompatibilityLayer):
31+
class HttpStatusError(Exception):
32+
status_code: int
33+
message: str
34+
35+
def __init__(self, status_code: int, message: str):
36+
self.status_code = status_code
37+
self.message = message
38+
super().__init__(message)
39+
3140
@staticmethod
3241
def repodata_info() -> dict[str, str]:
3342
return REPODATA_INFO
@@ -50,7 +59,11 @@ async def fetch_bytes(url: str, kwargs: dict[str, str]) -> bytes:
5059
async def fetch_string_and_headers(
5160
url: str, kwargs: dict[str, str]
5261
) -> tuple[str, dict[str, str]]:
53-
response = await pyfetch(url, **kwargs)
62+
try:
63+
response = await pyfetch(url, **kwargs)
64+
response.raise_for_status()
65+
except HttpStatusError as e:
66+
raise CompatibilityInPyodide.HttpStatusError(e.status, str(e)) from e
5467

5568
content = await response.string()
5669
headers: dict[str, str] = response.headers

micropip/_compat/_compat_not_in_pyodide.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from pathlib import Path
33
from typing import IO, TYPE_CHECKING, Any
4+
from urllib.error import HTTPError
45
from urllib.request import Request, urlopen
56
from urllib.response import addinfourl
67

@@ -15,6 +16,15 @@ class CompatibilityNotInPyodide(CompatibilityLayer):
1516
# Vendored from packaging
1617
_canonicalize_regex = re.compile(r"[-_.]+")
1718

19+
class HttpStatusError(Exception):
20+
status_code: int
21+
message: str
22+
23+
def __init__(self, status_code: int, message: str):
24+
self.status_code = status_code
25+
self.message = message
26+
super().__init__(message)
27+
1828
class loadedPackages(CompatibilityLayer.loadedPackages):
1929
@staticmethod
2030
def to_py():
@@ -40,7 +50,11 @@ async def fetch_bytes(url: str, kwargs: dict[str, Any]) -> bytes:
4050
async def fetch_string_and_headers(
4151
url: str, kwargs: dict[str, Any]
4252
) -> tuple[str, dict[str, str]]:
43-
response = CompatibilityNotInPyodide._fetch(url, kwargs=kwargs)
53+
try:
54+
response = CompatibilityNotInPyodide._fetch(url, kwargs=kwargs)
55+
except HTTPError as e:
56+
raise CompatibilityNotInPyodide.HttpStatusError(e.code, str(e)) from e
57+
4458
headers = {k.lower(): v for k, v in response.headers.items()}
4559
return response.read().decode(), headers
4660

micropip/_compat/compatibility_layer.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ class CompatibilityLayer(ABC):
1313
All of the following methods / properties must be implemented for use both inside and outside of pyodide.
1414
"""
1515

16+
class HttpStatusError(ABC, Exception):
17+
status_code: int
18+
message: str
19+
20+
@abstractmethod
21+
def __init__(self, status_code: int, message: str):
22+
pass
23+
1624
class loadedPackages(ABC):
1725
@staticmethod
1826
@abstractmethod

0 commit comments

Comments
 (0)