diff --git a/src/poetry/core/packages/utils/link.py b/src/poetry/core/packages/utils/link.py index a06348584..6f93c447a 100644 --- a/src/poetry/core/packages/utils/link.py +++ b/src/poetry/core/packages/utils/link.py @@ -16,6 +16,7 @@ def __init__( url: str, comes_from: Any | None = None, requires_python: str | None = None, + metadata: str | None = None, ) -> None: """ Object representing a parsed link from https://pypi.python.org/simple/* @@ -28,6 +29,11 @@ def __init__( String containing the `Requires-Python` metadata field, specified in PEP 345. This may be specified by a data-requires-python attribute in the HTML link tag, as described in PEP 503. + metadata: + String of the syntax `=` representing the hash + of the Core Metadata file. This may be specified by a + data-dist-info-metadata attribute in the HTML link tag, as described + in PEP 658. """ # url can be a UNC windows share @@ -37,6 +43,7 @@ def __init__( self.url = url self.comes_from = comes_from self.requires_python = requires_python if requires_python else None + self._metadata = metadata def __str__(self) -> str: if self.requires_python: @@ -136,6 +143,32 @@ def subdirectory_fragment(self) -> str | None: _hash_re = re.compile(r"(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)") + @property + def has_metadata(self) -> bool: + return self._metadata is not None and (self.is_wheel or self.is_sdist) + + @property + def metadata_url(self) -> str | None: + if self.has_metadata: + return f"{self.url_without_fragment.split('?', 1)[0]}.metadata" + return None + + @property + def metadata_hash(self) -> str | None: + if self.has_metadata: + match = self._hash_re.search(self._metadata or "") + if match: + return match.group(2) + return None + + @property + def metadata_hash_name(self) -> str | None: + if self.has_metadata: + match = self._hash_re.search(self._metadata or "") + if match: + return match.group(1) + return None + @property def hash(self) -> str | None: match = self._hash_re.search(self.url) diff --git a/tests/packages/utils/test_utils_link.py b/tests/packages/utils/test_utils_link.py index c7067bad0..380b263fd 100644 --- a/tests/packages/utils/test_utils_link.py +++ b/tests/packages/utils/test_utils_link.py @@ -4,20 +4,89 @@ from hashlib import sha256 +import pytest + from poetry.core.packages.utils.link import Link -def make_url(ext: str) -> Link: - checksum = sha256(str(uuid.uuid4()).encode()) +def make_checksum() -> str: + return sha256(str(uuid.uuid4()).encode()).hexdigest() + + +@pytest.fixture() +def file_checksum() -> str: + return make_checksum() + + +@pytest.fixture() +def metadata_checksum() -> str: + return make_checksum() + + +def make_url( + ext: str, file_checksum: str | None = None, metadata_checksum: str | None = None +) -> Link: + file_checksum = file_checksum or make_checksum() return Link( "https://files.pythonhosted.org/packages/16/52/dead/" - f"demo-1.0.0.{ext}#sha256={checksum}" + f"demo-1.0.0.{ext}#sha256={file_checksum}", + metadata=f"sha256={metadata_checksum}" if metadata_checksum else None, ) -def test_package_link_is_checks() -> None: - assert make_url("egg").is_egg - assert make_url("tar.gz").is_sdist - assert make_url("zip").is_sdist - assert make_url("exe").is_wininst - assert make_url("cp36-cp36m-manylinux1_x86_64.whl").is_wheel +def test_package_link_hash(file_checksum: str) -> None: + link = make_url(ext="whl", file_checksum=file_checksum) + assert link.hash_name == "sha256" + assert link.hash == file_checksum + assert link.show_url == "demo-1.0.0.whl" + + # this is legacy PEP 503, no metadata hash is present + assert not link.has_metadata + assert not link.metadata_url + assert not link.metadata_hash + assert not link.metadata_hash_name + + +@pytest.mark.parametrize( + ("ext", "check"), + [ + ("whl", "wheel"), + ("egg", "egg"), + ("tar.gz", "sdist"), + ("zip", "sdist"), + ("cp36-cp36m-manylinux1_x86_64.whl", "wheel"), + ], +) +def test_package_link_is_checks(ext: str, check: str) -> None: + link = make_url(ext=ext) + assert getattr(link, f"is_{check}") + + +@pytest.mark.parametrize( + ("ext", "has_metadata"), + [("whl", True), ("egg", False), ("tar.gz", True), ("zip", True)], +) +def test_package_link_pep658( + ext: str, has_metadata: bool, metadata_checksum: str +) -> None: + link = make_url(ext=ext, metadata_checksum=metadata_checksum) + + if has_metadata: + assert link.has_metadata + assert link.metadata_url == f"{link.url_without_fragment}.metadata" + assert link.metadata_hash == metadata_checksum + assert link.metadata_hash_name == "sha256" + else: + assert not link.has_metadata + assert not link.metadata_url + assert not link.metadata_hash + assert not link.metadata_hash_name + + +def test_package_link_pep658_no_default_metadata() -> None: + link = make_url(ext="whl") + + assert not link.has_metadata + assert not link.metadata_url + assert not link.metadata_hash + assert not link.metadata_hash_name