Skip to content

Commit 48152bb

Browse files
committed
pep 658 hashes are not URL fragment hashes
1 parent 6e5d467 commit 48152bb

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

src/pip/_internal/models/link.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class LinkHash:
5555
name: str
5656
value: str
5757

58-
_hash_re = re.compile(
58+
_hash_url_fragment_re = re.compile(
5959
# NB: we do not validate that the second group (.*) is a valid hex
6060
# digest. Instead, we simply keep that string in this class, and then check it
6161
# against Hashes when hash-checking is needed. This is easier to debug than
@@ -67,13 +67,26 @@ class LinkHash:
6767
)
6868

6969
def __post_init__(self) -> None:
70-
assert self._hash_re.match(f"#{self.name}={self.value}")
70+
assert self._hash_url_fragment_re.match(f"#{self.name}={self.value}")
71+
72+
@classmethod
73+
def parse_pep658_hash(cls, dist_info_metadata: str) -> Optional["LinkHash"]:
74+
"""Parse a PEP 658 data-dist-info-metadata hash."""
75+
if dist_info_metadata == "true":
76+
return None
77+
try:
78+
name, value = dist_info_metadata.split("=", 1)
79+
except ValueError:
80+
return None
81+
if name not in _SUPPORTED_HASHES:
82+
return None
83+
return cls(name=name, value=value)
7184

7285
@classmethod
7386
@functools.lru_cache(maxsize=None)
74-
def split_hash_name_and_value(cls, url: str) -> Optional["LinkHash"]:
87+
def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]:
7588
"""Search a string for a checksum algorithm name and encoded output value."""
76-
match = cls._hash_re.search(url)
89+
match = cls._hash_url_fragment_re.search(url)
7790
if match is None:
7891
return None
7992
name, value = match.groups()
@@ -217,7 +230,7 @@ def __init__(
217230
# trying to set a new value.
218231
self._url = url
219232

220-
link_hash = LinkHash.split_hash_name_and_value(url)
233+
link_hash = LinkHash.find_hash_url_fragment(url)
221234
hashes_from_link = {} if link_hash is None else link_hash.as_dict()
222235
if hashes is None:
223236
self._hashes = hashes_from_link
@@ -402,15 +415,10 @@ def metadata_link(self) -> Optional["Link"]:
402415
if self.dist_info_metadata is None:
403416
return None
404417
metadata_url = f"{self.url_without_fragment}.metadata"
405-
# If data-dist-info-metadata="true" is set, then the metadata file exists,
406-
# but there is no information about its checksum or anything else.
407-
if self.dist_info_metadata != "true":
408-
link_hash = LinkHash.split_hash_name_and_value(self.dist_info_metadata)
409-
else:
410-
link_hash = None
411-
if link_hash is None:
418+
metadata_link_hash = LinkHash.parse_pep658_hash(self.dist_info_metadata)
419+
if metadata_link_hash is None:
412420
return Link(metadata_url)
413-
return Link(metadata_url, hashes=link_hash.as_dict())
421+
return Link(metadata_url, hashes=metadata_link_hash.as_dict())
414422

415423
def as_hashes(self) -> Hashes:
416424
return Hashes({k: [v] for k, v in self._hashes.items()})

tests/unit/test_collector.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,4 +1073,20 @@ def expand_path(path: str) -> str:
10731073
],
10741074
)
10751075
def test_link_hash_parsing(url: str, result: Optional[LinkHash]) -> None:
1076-
assert LinkHash.split_hash_name_and_value(url) == result
1076+
assert LinkHash.find_hash_url_fragment(url) == result
1077+
1078+
1079+
@pytest.mark.parametrize(
1080+
"dist_info_metadata, result",
1081+
[
1082+
("sha256=aa113592bbe", LinkHash("sha256", "aa113592bbe")),
1083+
("sha500=aa113592bbe", None),
1084+
("true", None),
1085+
("", None),
1086+
("aa113592bbe", None),
1087+
],
1088+
)
1089+
def test_pep658_hash_parsing(
1090+
dist_info_metadata: str, result: Optional[LinkHash]
1091+
) -> None:
1092+
assert LinkHash.parse_pep658_hash(dist_info_metadata) == result

0 commit comments

Comments
 (0)