1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Merge link_hash back into _hashes

Commit bad03ef931 introduced the new
link_hash attribute that holds the link's hash info, but that attribute
does the same thing as _hashes, and some existing usages still populate
that old attribute. Since the plural variant covers more use cases (a
file can be hashed with multiple algorithms), we restore the old logic
that uses _hashes before the commit, and consolidate link_hash back into
that attribute.
This commit is contained in:
Tzu-ping Chung 2023-01-04 18:18:07 +08:00
parent ba38c33b6b
commit 0233bf2757
2 changed files with 34 additions and 36 deletions

View file

@ -79,6 +79,9 @@ class LinkHash:
name, value = match.groups()
return cls(name=name, value=value)
def as_dict(self) -> Dict[str, str]:
return {self.name: self.value}
def as_hashes(self) -> Hashes:
"""Return a Hashes instance which checks only for the current hash."""
return Hashes({self.name: [self.value]})
@ -165,7 +168,6 @@ class Link(KeyBasedCompareMixin):
"requires_python",
"yanked_reason",
"dist_info_metadata",
"link_hash",
"cache_link_parsing",
"egg_fragment",
]
@ -177,7 +179,6 @@ class Link(KeyBasedCompareMixin):
requires_python: Optional[str] = None,
yanked_reason: Optional[str] = None,
dist_info_metadata: Optional[str] = None,
link_hash: Optional[LinkHash] = None,
cache_link_parsing: bool = True,
hashes: Optional[Mapping[str, str]] = None,
) -> None:
@ -200,16 +201,11 @@ class Link(KeyBasedCompareMixin):
attribute, if present, in a simple repository HTML link. This may be parsed
into its own `Link` by `self.metadata_link()`. See PEP 658 for more
information and the specification.
:param link_hash: a checksum for the content the link points to. If not
provided, this will be extracted from the link URL, if the URL has
any checksum.
:param cache_link_parsing: A flag that is used elsewhere to determine
whether resources retrieved from this link
should be cached. PyPI index urls should
generally have this set to False, for
example.
whether resources retrieved from this link should be cached. PyPI
URLs should generally have this set to False, for example.
:param hashes: A mapping of hash names to digests to allow us to
determine the validity of a download.
determine the validity of a download.
"""
# url can be a UNC windows share
@ -220,13 +216,18 @@ class Link(KeyBasedCompareMixin):
# Store the url as a private attribute to prevent accidentally
# trying to set a new value.
self._url = url
self._hashes = hashes if hashes is not None else {}
link_hash = LinkHash.split_hash_name_and_value(url)
hashes_from_link = {} if link_hash is None else link_hash.as_dict()
if hashes is None:
self._hashes = hashes_from_link
else:
self._hashes = {**hashes, **hashes_from_link}
self.comes_from = comes_from
self.requires_python = requires_python if requires_python else None
self.yanked_reason = yanked_reason
self.dist_info_metadata = dist_info_metadata
self.link_hash = link_hash or LinkHash.split_hash_name_and_value(self._url)
super().__init__(key=url, defining_class=Link)
@ -401,29 +402,26 @@ class Link(KeyBasedCompareMixin):
if self.dist_info_metadata is None:
return None
metadata_url = f"{self.url_without_fragment}.metadata"
link_hash: Optional[LinkHash] = None
# If data-dist-info-metadata="true" is set, then the metadata file exists,
# but there is no information about its checksum or anything else.
if self.dist_info_metadata != "true":
link_hash = LinkHash.split_hash_name_and_value(self.dist_info_metadata)
return Link(metadata_url, link_hash=link_hash)
else:
link_hash = None
if link_hash is None:
return Link(metadata_url)
return Link(metadata_url, hashes=link_hash.as_dict())
def as_hashes(self) -> Optional[Hashes]:
if self.link_hash is not None:
return self.link_hash.as_hashes()
return None
def as_hashes(self) -> Hashes:
return Hashes({k: [v] for k, v in self._hashes.items()})
@property
def hash(self) -> Optional[str]:
if self.link_hash is not None:
return self.link_hash.value
return None
return next(iter(self._hashes.values()), None)
@property
def hash_name(self) -> Optional[str]:
if self.link_hash is not None:
return self.link_hash.name
return None
return next(iter(self._hashes), None)
@property
def show_url(self) -> str:
@ -452,15 +450,15 @@ class Link(KeyBasedCompareMixin):
@property
def has_hash(self) -> bool:
return self.link_hash is not None
return bool(self._hashes)
def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool:
"""
Return True if the link has a hash and it is allowed by `hashes`.
"""
if self.link_hash is None:
if hashes is None:
return False
return self.link_hash.is_hash_allowed(hashes)
return any(hashes.is_hash_allowed(k, v) for k, v in self._hashes.items())
class _CleanResult(NamedTuple):

View file

@ -6,7 +6,7 @@ import re
import uuid
from pathlib import Path
from textwrap import dedent
from typing import List, Optional, Tuple
from typing import Dict, List, Optional, Tuple
from unittest import mock
import pytest
@ -538,7 +538,7 @@ def test_parse_links_json() -> None:
metadata_link.url
== "https://example.com/files/holygrail-1.0-py3-none-any.whl.metadata"
)
assert metadata_link.link_hash == LinkHash("sha512", "aabdd41")
assert metadata_link._hashes == {"sha512": "aabdd41"}
@pytest.mark.parametrize(
@ -575,41 +575,41 @@ _pkg1_requirement = Requirement("pkg1==1.0")
@pytest.mark.parametrize(
"anchor_html, expected, link_hash",
"anchor_html, expected, hashes",
[
# Test not present.
(
'<a href="/pkg1-1.0.tar.gz"></a>',
None,
None,
{},
),
# Test with value "true".
(
'<a href="/pkg1-1.0.tar.gz" data-dist-info-metadata="true"></a>',
"true",
None,
{},
),
# Test with a provided hash value.
(
'<a href="/pkg1-1.0.tar.gz" data-dist-info-metadata="sha256=aa113592bbe"></a>', # noqa: E501
"sha256=aa113592bbe",
None,
{},
),
# Test with a provided hash value for both the requirement as well as metadata.
(
'<a href="/pkg1-1.0.tar.gz#sha512=abc132409cb" data-dist-info-metadata="sha256=aa113592bbe"></a>', # noqa: E501
"sha256=aa113592bbe",
LinkHash("sha512", "abc132409cb"),
{"sha512": "abc132409cb"},
),
],
)
def test_parse_links__dist_info_metadata(
anchor_html: str,
expected: Optional[str],
link_hash: Optional[LinkHash],
hashes: Dict[str, str],
) -> None:
link = _test_parse_links_data_attribute(anchor_html, "dist_info_metadata", expected)
assert link.link_hash == link_hash
assert link._hashes == hashes
def test_parse_links_caches_same_page_by_url() -> None: