From 90e2c1ed9827534f5067624d13b0a22fc1e03390 Mon Sep 17 00:00:00 2001 From: q0w <43147888+q0w@users.noreply.github.com> Date: Mon, 14 Mar 2022 10:42:07 +0300 Subject: [PATCH 1/3] Pick up constraints for template --- news/9243.bugfix.rst | 2 + src/pip/_internal/req/req_install.py | 5 +-- .../resolution/resolvelib/factory.py | 17 +++----- src/pip/_internal/utils/hashes.py | 4 ++ src/pip/_internal/utils/packaging.py | 13 +++++++ tests/functional/test_new_resolver_hashes.py | 39 ++++++++++++++++--- 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 news/9243.bugfix.rst diff --git a/news/9243.bugfix.rst b/news/9243.bugfix.rst new file mode 100644 index 00000000000..e83bdb09cfe --- /dev/null +++ b/news/9243.bugfix.rst @@ -0,0 +1,2 @@ +New resolver: Pick up hash declarations in constraints files and use them to +filter available distributions. diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 02dbda1941f..637b6ce10af 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -52,7 +52,7 @@ hide_url, redact_auth_from_url, ) -from pip._internal.utils.packaging import safe_extra +from pip._internal.utils.packaging import is_pinned, safe_extra from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds from pip._internal.utils.virtualenv import running_under_virtualenv @@ -238,8 +238,7 @@ def is_pinned(self) -> bool: For example, some-package==1.2 is pinned; some-package>1.2 is not. """ - specifiers = self.specifier - return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="} + return is_pinned(self.specifier) def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool: if not extras_requested: diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 4569033db0a..3f2c348e7dd 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -45,7 +45,7 @@ from pip._internal.resolution.base import InstallRequirementProvider from pip._internal.utils.compatibility_tags import get_supported from pip._internal.utils.hashes import Hashes -from pip._internal.utils.packaging import get_requirement +from pip._internal.utils.packaging import get_requirement, is_pinned from pip._internal.utils.virtualenv import running_under_virtualenv from .base import Candidate, CandidateVersion, Constraint, Requirement @@ -303,19 +303,12 @@ def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]: # solely satisfied by a yanked release. all_yanked = all(ican.link.is_yanked for ican in icans) - def is_pinned(specifier: SpecifierSet) -> bool: - for sp in specifier: - if sp.operator == "===": - return True - if sp.operator != "==": - continue - if sp.version.endswith(".*"): - continue - return True - return False - pinned = is_pinned(specifier) + assert template.req, "Candidates found on index must be PEP 508" + template.req.specifier = specifier + template.hash_options = hashes.allowed + # PackageFinder returns earlier versions first, so we reverse. for ican in reversed(icans): if not (all_yanked and pinned) and ican.link.is_yanked: diff --git a/src/pip/_internal/utils/hashes.py b/src/pip/_internal/utils/hashes.py index 82eb035a06e..2f89bbf22f5 100644 --- a/src/pip/_internal/utils/hashes.py +++ b/src/pip/_internal/utils/hashes.py @@ -63,6 +63,10 @@ def __and__(self, other: "Hashes") -> "Hashes": def digest_count(self) -> int: return sum(len(digests) for digests in self._allowed.values()) + @property + def allowed(self) -> Dict[str, List[str]]: + return self._allowed + def is_hash_allowed(self, hash_name: str, hex_digest: str) -> bool: """Return whether the given hex digest is allowed.""" return hex_digest in self._allowed.get(hash_name, []) diff --git a/src/pip/_internal/utils/packaging.py b/src/pip/_internal/utils/packaging.py index b9f6af4d174..7c77371a205 100644 --- a/src/pip/_internal/utils/packaging.py +++ b/src/pip/_internal/utils/packaging.py @@ -5,6 +5,7 @@ from pip._vendor.packaging import specifiers, version from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import SpecifierSet NormalizedExtra = NewType("NormalizedExtra", str) @@ -55,3 +56,15 @@ def safe_extra(extra: str) -> NormalizedExtra: the same to either ``canonicalize_name`` or ``_egg_link_name``. """ return cast(NormalizedExtra, re.sub("[^A-Za-z0-9.-]+", "_", extra).lower()) + + +def is_pinned(specifier: SpecifierSet) -> bool: + for sp in specifier: + if sp.operator == "===": + return True + if sp.operator != "==": + continue + if sp.version.endswith(".*"): + continue + return True + return False diff --git a/tests/functional/test_new_resolver_hashes.py b/tests/functional/test_new_resolver_hashes.py index 80ed86219d4..95ccd373f55 100644 --- a/tests/functional/test_new_resolver_hashes.py +++ b/tests/functional/test_new_resolver_hashes.py @@ -223,11 +223,9 @@ def test_new_resolver_hash_intersect_empty_from_constraint( expect_error=True, ) - message = ( - "Hashes are required in --require-hashes mode, but they are missing " - "from some requirements." - ) - assert message in result.stderr, str(result) + assert ( + "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE." + ) in result.stderr, str(result) @pytest.mark.parametrize("constrain_by_hash", [False, True]) @@ -373,3 +371,34 @@ def test_new_resolver_hash_with_extras(script: PipTestEnvironment) -> None: child="0.1.0", extra="0.1.0", ) + + +def test_new_resolver_hash_with_pin(script: PipTestEnvironment) -> None: + find_links = _create_find_links(script) + + requirements_txt = script.scratch_path / "requirements.txt" + requirements_txt.write_text("base") + + constraints_txt = script.scratch_path / "constraints.txt" + constraints_txt.write_text( + """ + base==0.1.0 --hash=sha256:{sdist_hash} --hash=sha256:{wheel_hash} + """.format( + sdist_hash=find_links.sdist_hash, + wheel_hash=find_links.wheel_hash, + ) + ) + + script.pip( + "install", + "--no-cache-dir", + "--no-index", + "--find-links", + find_links.index_html, + "--requirement", + requirements_txt, + "--constraint", + constraints_txt, + ) + + script.assert_installed(base="0.1.0") From 8b0abe360d369001b7c8262462fd77bc7c7e49a8 Mon Sep 17 00:00:00 2001 From: q0w <43147888+q0w@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:24:12 +0300 Subject: [PATCH 2/3] Check if template is pinned --- src/pip/_internal/resolution/resolvelib/factory.py | 7 ++++--- tests/functional/test_new_resolver_hashes.py | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 3f2c348e7dd..3cfcac865ff 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -305,9 +305,10 @@ def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]: pinned = is_pinned(specifier) - assert template.req, "Candidates found on index must be PEP 508" - template.req.specifier = specifier - template.hash_options = hashes.allowed + if not template.is_pinned: + assert template.req, "Candidates found on index must be PEP 508" + template.req.specifier = specifier + template.hash_options = hashes.allowed # PackageFinder returns earlier versions first, so we reverse. for ican in reversed(icans): diff --git a/tests/functional/test_new_resolver_hashes.py b/tests/functional/test_new_resolver_hashes.py index 95ccd373f55..008a4284c1d 100644 --- a/tests/functional/test_new_resolver_hashes.py +++ b/tests/functional/test_new_resolver_hashes.py @@ -223,9 +223,11 @@ def test_new_resolver_hash_intersect_empty_from_constraint( expect_error=True, ) - assert ( - "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE." - ) in result.stderr, str(result) + message = ( + "Hashes are required in --require-hashes mode, but they are missing " + "from some requirements." + ) + assert message in result.stderr, str(result) @pytest.mark.parametrize("constrain_by_hash", [False, True]) From e54b4428e27d496f97093e39ed9297b6ce00a09a Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 16 Mar 2022 07:42:47 +0000 Subject: [PATCH 3/3] Update news/9243.bugfix.rst --- news/9243.bugfix.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/news/9243.bugfix.rst b/news/9243.bugfix.rst index e83bdb09cfe..2e588c90534 100644 --- a/news/9243.bugfix.rst +++ b/news/9243.bugfix.rst @@ -1,2 +1 @@ -New resolver: Pick up hash declarations in constraints files and use them to -filter available distributions. +Filter available distributions using hash declarations from constraints files.