Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

utils.link: add PEP 658 (metadata) support #333

Merged
merged 1 commit into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/poetry/core/packages/utils/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(
url: str,
comes_from: Any | None = None,
requires_python: str | None = None,
metadata: str | bool | None = None,
) -> None:
"""
Object representing a parsed link from https://pypi.python.org/simple/*
Expand All @@ -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 `<hashname>=<hashvalue>` 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
Expand All @@ -38,6 +44,13 @@ def __init__(
self.comes_from = comes_from
self.requires_python = requires_python if requires_python else None

if isinstance(metadata, str):
metadata = {"true": True, "": False, "false": False}.get(
metadata.strip().lower(), metadata
)

self._metadata = metadata

def __str__(self) -> str:
if self.requires_python:
rp = f" (requires-python:{self.requires_python})"
Expand Down Expand Up @@ -136,6 +149,34 @@ 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:
if self._metadata is None:
return False
return bool(self._metadata) 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"
abn marked this conversation as resolved.
Show resolved Hide resolved
return None

@property
def metadata_hash(self) -> str | None:
if self.has_metadata and isinstance(self._metadata, str):
match = self._hash_re.search(self._metadata)
if match:
return match.group(2)
return None

@property
def metadata_hash_name(self) -> str | None:
if self.has_metadata and isinstance(self._metadata, str):
match = self._hash_re.search(self._metadata)
if match:
return match.group(1)
return None

@property
def hash(self) -> str | None:
match = self._hash_re.search(self.url)
Expand Down
115 changes: 106 additions & 9 deletions tests/packages/utils/test_utils_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,117 @@

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_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
abn marked this conversation as resolved.
Show resolved Hide resolved


@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(
abn marked this conversation as resolved.
Show resolved Hide resolved
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


@pytest.mark.parametrize(
("metadata", "has_metadata"),
[
("true", True),
("false", False),
("", False),
],
)
def test_package_link_pep653_non_hash_metadata_value(
file_checksum: str, metadata: str | bool, has_metadata: bool
) -> None:
link = Link(
"https://files.pythonhosted.org/packages/16/52/dead/"
f"demo-1.0.0.whl#sha256={file_checksum}",
metadata=metadata,
)

if has_metadata:
assert link.has_metadata
assert link.metadata_url == f"{link.url_without_fragment}.metadata"
else:
assert not link.has_metadata
assert not link.metadata_url

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
assert not link.metadata_hash
assert not link.metadata_hash_name