diff --git a/src/pip/_internal/index.py b/src/pip/_internal/index.py index aea4258e8..f601f1831 100644 --- a/src/pip/_internal/index.py +++ b/src/pip/_internal/index.py @@ -550,7 +550,8 @@ class CandidateEvaluator(object): target_python=None, # type: Optional[TargetPython] prefer_binary=False, # type: bool allow_all_prereleases=False, # type: bool - hashes=None, # type: Optional[Hashes] + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] ): # type: (...) -> CandidateEvaluator """Create a CandidateEvaluator object. @@ -562,12 +563,15 @@ class CandidateEvaluator(object): """ if target_python is None: target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() supported_tags = target_python.get_tags() return cls( project_name=project_name, supported_tags=supported_tags, + specifier=specifier, prefer_binary=prefer_binary, allow_all_prereleases=allow_all_prereleases, hashes=hashes, @@ -577,6 +581,7 @@ class CandidateEvaluator(object): self, project_name, # type: str supported_tags, # type: List[Pep425Tag] + specifier, # type: specifiers.BaseSpecifier prefer_binary=False, # type: bool allow_all_prereleases=False, # type: bool hashes=None, # type: Optional[Hashes] @@ -590,12 +595,12 @@ class CandidateEvaluator(object): self._hashes = hashes self._prefer_binary = prefer_binary self._project_name = project_name + self._specifier = specifier self._supported_tags = supported_tags def get_applicable_candidates( self, candidates, # type: List[InstallationCandidate] - specifier, # type: specifiers.BaseSpecifier ): # type: (...) -> List[InstallationCandidate] """ @@ -603,6 +608,7 @@ class CandidateEvaluator(object): """ # Using None infers from the specifier instead. allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier versions = { str(v) for v in specifier.filter( # We turn the version object into a str here because otherwise @@ -631,7 +637,6 @@ class CandidateEvaluator(object): def make_found_candidates( self, candidates, # type: List[InstallationCandidate] - specifier=None, # type: Optional[specifiers.BaseSpecifier] ): # type: (...) -> FoundCandidates """ @@ -641,13 +646,7 @@ class CandidateEvaluator(object): (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable versions. """ - if specifier is None: - specifier = specifiers.SpecifierSet() - - applicable_candidates = self.get_applicable_candidates( - candidates=candidates, - specifier=specifier, - ) + applicable_candidates = self.get_applicable_candidates(candidates) return FoundCandidates( candidates, @@ -1149,8 +1148,9 @@ class PackageFinder(object): def make_candidate_evaluator( self, - project_name, # type: str - hashes=None, # type: Optional[Hashes] + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] ): # type: (...) -> CandidateEvaluator """Create a CandidateEvaluator object to use. @@ -1161,6 +1161,7 @@ class PackageFinder(object): target_python=self._target_python, prefer_binary=candidate_prefs.prefer_binary, allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, hashes=hashes, ) @@ -1182,11 +1183,10 @@ class PackageFinder(object): candidates = self.find_all_candidates(project_name) candidate_evaluator = self.make_candidate_evaluator( project_name=project_name, + specifier=specifier, hashes=hashes, ) - return candidate_evaluator.make_found_candidates( - candidates, specifier=specifier, - ) + return candidate_evaluator.make_found_candidates(candidates) def find_requirement(self, req, upgrade): # type: (InstallRequirement, bool) -> Optional[Link] diff --git a/tests/unit/test_finder.py b/tests/unit/test_finder.py index a2210a9ec..8810f30b9 100644 --- a/tests/unit/test_finder.py +++ b/tests/unit/test_finder.py @@ -3,6 +3,7 @@ import sys import pytest from mock import Mock, patch +from pip._vendor.packaging.specifiers import SpecifierSet from pkg_resources import parse_version import pip._internal.pep425tags @@ -216,7 +217,10 @@ class TestWheel: ('pyT', 'TEST', 'any'), ('pyT', 'none', 'any'), ] - evaluator = CandidateEvaluator('my-project', supported_tags=valid_tags) + specifier = SpecifierSet() + evaluator = CandidateEvaluator( + 'my-project', supported_tags=valid_tags, specifier=specifier, + ) sort_key = evaluator._sort_key results = sorted(links, key=sort_key, reverse=True) results2 = sorted(reversed(links), key=sort_key, reverse=True) diff --git a/tests/unit/test_index.py b/tests/unit/test_index.py index 2093dbafd..e3b36d6dc 100644 --- a/tests/unit/test_index.py +++ b/tests/unit/test_index.py @@ -299,14 +299,17 @@ class TestCandidateEvaluator: def test_create(self, allow_all_prereleases, prefer_binary): target_python = TargetPython() target_python._valid_tags = [('py36', 'none', 'any')] + specifier = SpecifierSet() evaluator = CandidateEvaluator.create( project_name='my-project', target_python=target_python, allow_all_prereleases=allow_all_prereleases, prefer_binary=prefer_binary, + specifier=specifier, ) assert evaluator._allow_all_prereleases == allow_all_prereleases assert evaluator._prefer_binary == prefer_binary + assert evaluator._specifier is specifier assert evaluator._supported_tags == [('py36', 'none', 'any')] def test_create__target_python_none(self): @@ -317,16 +320,25 @@ class TestCandidateEvaluator: expected_tags = get_supported() assert evaluator._supported_tags == expected_tags + def test_create__specifier_none(self): + """ + Test passing specifier=None. + """ + evaluator = CandidateEvaluator.create('my-project') + expected_specifier = SpecifierSet() + assert evaluator._specifier == expected_specifier + def test_get_applicable_candidates(self): specifier = SpecifierSet('<= 1.11') versions = ['1.10', '1.11', '1.12'] candidates = [ make_mock_candidate(version) for version in versions ] - evaluator = CandidateEvaluator.create('my-project') - actual = evaluator.get_applicable_candidates( - candidates, specifier=specifier, + evaluator = CandidateEvaluator.create( + 'my-project', + specifier=specifier, ) + actual = evaluator.get_applicable_candidates(candidates) expected_applicable = candidates[:2] assert [str(c.version) for c in expected_applicable] == [ '1.10', @@ -347,7 +359,6 @@ class TestCandidateEvaluator: """ Test a non-None hashes value. """ - # specifier = SpecifierSet('<= 1.1') candidates = [ make_mock_candidate('1.0'), make_mock_candidate('1.1', hex_digest=(64 * 'a')), @@ -357,10 +368,12 @@ class TestCandidateEvaluator: 'sha256': [64 * 'b'], } hashes = Hashes(hashes_data) - evaluator = CandidateEvaluator.create('my-project', hashes=hashes) - actual = evaluator.get_applicable_candidates( - candidates, specifier=specifier, + evaluator = CandidateEvaluator.create( + 'my-project', + specifier=specifier, + hashes=hashes, ) + actual = evaluator.get_applicable_candidates(candidates) actual_versions = [str(c.version) for c in actual] assert actual_versions == expected_versions @@ -370,10 +383,11 @@ class TestCandidateEvaluator: candidates = [ make_mock_candidate(version) for version in versions ] - evaluator = CandidateEvaluator.create('my-project') - found_candidates = evaluator.make_found_candidates( - candidates, specifier=specifier, + evaluator = CandidateEvaluator.create( + 'my-project', + specifier=specifier, ) + found_candidates = evaluator.make_found_candidates(candidates) assert found_candidates._candidates == candidates assert found_candidates._evaluator is evaluator @@ -776,15 +790,19 @@ class TestPackageFinder: candidate_prefs=candidate_prefs, ) + specifier = SpecifierSet() # Pass hashes to check that _hashes is set. hashes = Hashes({'sha256': [64 * 'a']}) evaluator = finder.make_candidate_evaluator( - 'my-project', hashes=hashes, + 'my-project', + specifier=specifier, + hashes=hashes, ) assert evaluator._allow_all_prereleases == allow_all_prereleases assert evaluator._hashes == hashes assert evaluator._prefer_binary == prefer_binary assert evaluator._project_name == 'my-project' + assert evaluator._specifier is specifier assert evaluator._supported_tags == [('py36', 'none', 'any')]