From ac172d91001e258beb8258a470c31de4f6ccd1db Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Mon, 21 Oct 2024 15:23:26 +0200 Subject: [PATCH] Add implied assertion in WheelInfo Some simple indexes (anaconda), only list the full-pathname of download URL (assuming same domain, which make sens). WheelInfo.download does not work on this case at it tries to make the requests on the current page domain. Thus we implicitely assume that URL start with http (well https:// would be better, but when you prototype local index could be http.) The error can be weird if we let it propagate (bad ziplife as you try to decode often a 404 html page as ZIP). This thus just add an assert at construction time to catch the error early and that we start with actual protocol. It should in the end be pushed earler in the code (likely at parsing time), where we are likely to know the index URL and be able to resolve URLs at that time. I think there was also a missing comma in test parametrisations, and I added a !r in a few places as I had some error with empty filenames, without quotes, so hard to see. The conftest was also update to explicitely pass a fake URL instead of just a filename. I'm not entirely sure we should allow constructing URLs with things that don't have a protocol though, as this can be confusing. --- micropip/_utils.py | 4 ++-- micropip/wheelinfo.py | 6 ++++++ tests/conftest.py | 2 +- tests/test_install.py | 2 +- tests/test_transaction.py | 10 +++++++--- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/micropip/_utils.py b/micropip/_utils.py index 9d6bc350..1167a010 100644 --- a/micropip/_utils.py +++ b/micropip/_utils.py @@ -142,9 +142,9 @@ def check_compatible(filename: str) -> None: try: tags = parse_tags(filename) except InvalidWheelFilename: - raise ValueError(f"Wheel filename is invalid: {filename}") from None + raise ValueError(f"Wheel filename is invalid: {filename!r}") from None except InvalidVersion: - raise ValueError(f"Wheel version is invalid: {filename}") from None + raise ValueError(f"Wheel version is invalid: {filename!r}") from None tag: Tag = next(iter(tags)) if "emscripten" not in tag.platform: diff --git a/micropip/wheelinfo.py b/micropip/wheelinfo.py index c6d255fa..197ea4cb 100644 --- a/micropip/wheelinfo.py +++ b/micropip/wheelinfo.py @@ -54,6 +54,9 @@ class WheelInfo: _dist_info: Path | None = None def __post_init__(self): + assert ( + self.url.startwith(p) for p in ("http:", "https:", "emfs:", "file:") + ), self.url self._project_name = safe_name(self.name) @classmethod @@ -63,7 +66,10 @@ def from_url(cls, url: str) -> "WheelInfo": See https://www.python.org/dev/peps/pep-0427/#file-name-convention """ parsed_url = urlparse(url) + if parsed_url.scheme == "": + url = "file:///" + url file_name = Path(parsed_url.path).name + assert file_name, (url, file_name, parsed_url) name, version, build, tags = parse_wheel_filename(file_name) return WheelInfo( name=name, diff --git a/tests/conftest.py b/tests/conftest.py index af55b88c..c704a572 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -270,7 +270,7 @@ def add_pkg_version( releases[version] = [ { "filename": filename, - "url": filename, + "url": f"http://fake.domain/f/{filename}", "digests": { "sha256": Wildcard(), }, diff --git a/tests/test_install.py b/tests/test_install.py index 111926a3..aa89b0e3 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -257,7 +257,7 @@ def _mock_fetch_bytes(arg, *args, **kwargs): msg = "Access-Control-Allow-Origin" with pytest.raises(ValueError, match=msg): - await micropip.install("htps://x.com/xxx-1.0.0-py3-none-any.whl") + await micropip.install("https://x.com/xxx-1.0.0-py3-none-any.whl") @pytest.mark.skip_refcount_check diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 326490d3..e3f36189 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -7,17 +7,21 @@ "path", [ SNOWBALL_WHEEL, - f"/{SNOWBALL_WHEEL}" f"a/{SNOWBALL_WHEEL}", + f"/{SNOWBALL_WHEEL}", + f"a/{SNOWBALL_WHEEL}", f"/a/{SNOWBALL_WHEEL}", f"//a/{SNOWBALL_WHEEL}", ], ) -@pytest.mark.parametrize("protocol", ["https:", "file:", "emfs:", ""]) +@pytest.mark.parametrize( + "protocol", + ["https:", "file:", "emfs:", ""], +) def test_parse_wheel_url1(protocol, path): pytest.importorskip("packaging") from micropip.transaction import WheelInfo - url = protocol + path + url = protocol + path if protocol else "file:///" + path wheel = WheelInfo.from_url(url) assert wheel.name == "snowballstemmer" assert str(wheel.version) == "2.0.0"