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

Merge constraints into a single SpecifierSet

This commit is contained in:
Paul Moore 2020-05-06 11:30:25 +01:00
parent ee4830b5ec
commit 64b3d1b278
6 changed files with 32 additions and 26 deletions

View file

@ -24,8 +24,8 @@ class Requirement(object):
# type: () -> str # type: () -> str
raise NotImplementedError("Subclass should override") raise NotImplementedError("Subclass should override")
def find_matches(self, constraints): def find_matches(self, constraint):
# type: (Sequence[SpecifierSet]) -> Sequence[Candidate] # type: (SpecifierSet) -> Sequence[Candidate]
raise NotImplementedError("Subclass should override") raise NotImplementedError("Subclass should override")
def is_satisfied_by(self, candidate): def is_satisfied_by(self, candidate):

View file

@ -182,7 +182,7 @@ class Factory(object):
cand = self._make_candidate_from_link( cand = self._make_candidate_from_link(
ireq.link, extras=set(ireq.extras), parent=ireq, ireq.link, extras=set(ireq.extras), parent=ireq,
) )
return ExplicitRequirement(cand, factory=self) return ExplicitRequirement(cand)
return SpecifierRequirement(ireq, factory=self) return SpecifierRequirement(ireq, factory=self)
def make_requirement_from_spec(self, specifier, comes_from): def make_requirement_from_spec(self, specifier, comes_from):

View file

@ -1,11 +1,10 @@
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.resolvelib.providers import AbstractProvider from pip._vendor.resolvelib.providers import AbstractProvider
from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING: if MYPY_CHECK_RUNNING:
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union from typing import Any, Dict, Optional, Sequence, Tuple, Union
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._internal.req.req_install import InstallRequirement from pip._internal.req.req_install import InstallRequirement
@ -17,7 +16,7 @@ class PipProvider(AbstractProvider):
def __init__( def __init__(
self, self,
factory, # type: Factory factory, # type: Factory
constraints, # type: Dict[str,List[SpecifierSet]] constraints, # type: Dict[str, SpecifierSet]
ignore_dependencies, # type: bool ignore_dependencies, # type: bool
): ):
# type: (...) -> None # type: (...) -> None
@ -45,8 +44,8 @@ class PipProvider(AbstractProvider):
def find_matches(self, requirement): def find_matches(self, requirement):
# type: (Requirement) -> Sequence[Candidate] # type: (Requirement) -> Sequence[Candidate]
constraints = self._constraints.get(requirement.name, []) constraint = self._constraints.get(requirement.name, SpecifierSet())
return requirement.find_matches(constraints) return requirement.find_matches(constraint)
def is_satisfied_by(self, requirement, candidate): def is_satisfied_by(self, requirement, candidate):
# type: (Requirement, Candidate) -> bool # type: (Requirement, Candidate) -> bool

View file

@ -17,10 +17,9 @@ if MYPY_CHECK_RUNNING:
class ExplicitRequirement(Requirement): class ExplicitRequirement(Requirement):
def __init__(self, candidate, factory): def __init__(self, candidate):
# type: (Candidate, Factory) -> None # type: (Candidate) -> None
self.candidate = candidate self.candidate = candidate
self._factory = factory
def __repr__(self): def __repr__(self):
# type: () -> str # type: () -> str
@ -35,9 +34,9 @@ class ExplicitRequirement(Requirement):
# No need to canonicalise - the candidate did this # No need to canonicalise - the candidate did this
return self.candidate.name return self.candidate.name
def find_matches(self, constraints): def find_matches(self, constraint):
# type: (Sequence[SpecifierSet]) -> Sequence[Candidate] # type: (Sequence[SpecifierSet]) -> Sequence[Candidate]
if constraints: if len(constraint) > 0:
raise InstallationError( raise InstallationError(
"Could not satisfy constraints for '{}': " "Could not satisfy constraints for '{}': "
"installation from path or url cannot be " "installation from path or url cannot be "
@ -75,12 +74,15 @@ class SpecifierRequirement(Requirement):
canonical_name = canonicalize_name(self._ireq.req.name) canonical_name = canonicalize_name(self._ireq.req.name)
return format_name(canonical_name, self.extras) return format_name(canonical_name, self.extras)
def find_matches(self, constraints): def find_matches(self, constraint):
# type: (Sequence[SpecifierSet]) -> Sequence[Candidate] # type: (SpecifierSet) -> Sequence[Candidate]
it = self._factory.iter_found_candidates(self._ireq, self.extras) return [
return [c for c in it if all( c
s.contains(c.version, prereleases=True) for s in constraints for c in self._factory.iter_found_candidates(
)] self._ireq, self.extras
)
if constraint.contains(c.version, prereleases=True)
]
def is_satisfied_by(self, candidate): def is_satisfied_by(self, candidate):
# type: (Candidate) -> bool # type: (Candidate) -> bool
@ -114,8 +116,10 @@ class RequiresPythonRequirement(Requirement):
# type: () -> str # type: () -> str
return self._candidate.name return self._candidate.name
def find_matches(self, constraints): def find_matches(self, constraint):
# type: (Sequence[SpecifierSet]) -> Sequence[Candidate] # type: (Sequence[SpecifierSet]) -> Sequence[Candidate]
assert len(constraint) == 0, \
"RequiresPythonRequirement cannot have constraints"
if self._candidate.version in self.specifier: if self._candidate.version in self.specifier:
return [self._candidate] return [self._candidate]
return [] return []

View file

@ -1,6 +1,5 @@
import functools import functools
import logging import logging
from collections import defaultdict
from pip._vendor import six from pip._vendor import six
from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.utils import canonicalize_name
@ -71,14 +70,17 @@ class Resolver(BaseResolver):
# The persistent state that we care about is `root_reqs`. # The persistent state that we care about is `root_reqs`.
assert len(self.factory.root_reqs) == 0, "Factory is being re-used" assert len(self.factory.root_reqs) == 0, "Factory is being re-used"
constraints = defaultdict(list) # type: Dict[str,List[SpecifierSet]] constraints = {} # type: Dict[str, SpecifierSet]
requirements = [] requirements = []
for req in root_reqs: for req in root_reqs:
if req.constraint: if req.constraint:
assert req.name assert req.name
assert req.specifier assert req.specifier
name = canonicalize_name(req.name) name = canonicalize_name(req.name)
constraints[name].append(req.specifier) if name in constraints:
constraints[name] = constraints[name] & req.specifier
else:
constraints[name] = req.specifier
else: else:
requirements.append( requirements.append(
self.factory.make_requirement_from_install_req(req) self.factory.make_requirement_from_install_req(req)

View file

@ -1,4 +1,5 @@
import pytest import pytest
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.resolvelib import BaseReporter, Resolver from pip._vendor.resolvelib import BaseReporter, Resolver
from pip._internal.resolution.resolvelib.base import Candidate from pip._internal.resolution.resolvelib.base import Candidate
@ -58,14 +59,14 @@ def test_new_resolver_correct_number_of_matches(test_cases, factory):
"""Requirements should return the correct number of candidates""" """Requirements should return the correct number of candidates"""
for spec, name, matches in test_cases: for spec, name, matches in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None) req = factory.make_requirement_from_spec(spec, comes_from=None)
assert len(req.find_matches([])) == matches assert len(req.find_matches(SpecifierSet())) == matches
def test_new_resolver_candidates_match_requirement(test_cases, factory): def test_new_resolver_candidates_match_requirement(test_cases, factory):
"""Candidates returned from find_matches should satisfy the requirement""" """Candidates returned from find_matches should satisfy the requirement"""
for spec, name, matches in test_cases: for spec, name, matches in test_cases:
req = factory.make_requirement_from_spec(spec, comes_from=None) req = factory.make_requirement_from_spec(spec, comes_from=None)
for c in req.find_matches([]): for c in req.find_matches(SpecifierSet()):
assert isinstance(c, Candidate) assert isinstance(c, Candidate)
assert req.is_satisfied_by(c) assert req.is_satisfied_by(c)