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

Merge pull request #11696 from uranusjr/fix-link-hashes

Merge link_hash back into _hashes
This commit is contained in:
Pradyun Gedam 2023-01-08 17:23:04 +00:00 committed by GitHub
commit 3b60e36289
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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: