Address review comments

This commit is contained in:
Paul Moore 2020-05-14 11:33:30 +01:00
parent 0db022fc42
commit 9cf1bed78d
7 changed files with 58 additions and 33 deletions

View File

@ -44,6 +44,11 @@ class Candidate(object):
# type: () -> _BaseVersion
raise NotImplementedError("Override in subclass")
@property
def is_installed(self):
# type: () -> bool
raise NotImplementedError("Override in subclass")
def get_dependencies(self):
# type: () -> Sequence[Requirement]
raise NotImplementedError("Override in subclass")

View File

@ -101,15 +101,10 @@ def make_install_req_from_dist(dist, parent):
return ireq
def is_already_installed(cand):
# type: (Candidate) -> bool
# For an ExtrasCandidate, we check the base
if isinstance(cand, ExtrasCandidate):
cand = cand.base
return isinstance(cand, AlreadyInstalledCandidate)
class _InstallRequirementBackedCandidate(Candidate):
# These are not installed
is_installed = False
def __init__(
self,
link, # type: Link
@ -279,6 +274,8 @@ class EditableCandidate(_InstallRequirementBackedCandidate):
class AlreadyInstalledCandidate(Candidate):
is_installed = True
def __init__(
self,
dist, # type: Distribution
@ -400,6 +397,11 @@ class ExtrasCandidate(Candidate):
# type: () -> _BaseVersion
return self.base.version
@property
def is_installed(self):
# type: () -> _BaseVersion
return self.base.is_installed
def get_dependencies(self):
# type: () -> Sequence[Requirement]
factory = self.base._factory
@ -436,6 +438,8 @@ class ExtrasCandidate(Candidate):
class RequiresPythonCandidate(Candidate):
is_installed = False
def __init__(self, py_version_info):
# type: (Optional[Tuple[int, ...]]) -> None
if py_version_info is not None:

View File

@ -114,6 +114,12 @@ class Factory(object):
def iter_found_candidates(self, ireq, extras):
# type: (InstallRequirement, Set[str]) -> Iterator[Candidate]
name = canonicalize_name(ireq.req.name)
# We use this to ensure that we only yield a single candidate for
# each version (the finder's preferred one for that version). The
# requirement needs to return only one candidate per version, so we
# implement that logic here so that requirements using this helper
# don't all have to do the same thing later.
seen_versions = set()
# Yield the installed version, if it matches, unless the user

View File

@ -3,8 +3,6 @@ from pip._vendor.resolvelib.providers import AbstractProvider
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
from .candidates import is_already_installed
if MYPY_CHECK_RUNNING:
from typing import Any, Dict, Optional, Sequence, Set, Tuple, Union
@ -40,16 +38,16 @@ class PipProvider(AbstractProvider):
constraints, # type: Dict[str, SpecifierSet]
ignore_dependencies, # type: bool
upgrade_strategy, # type: str
roots, # type: Set[str]
user_requested, # type: Set[str]
):
# type: (...) -> None
self._factory = factory
self._constraints = constraints
self._ignore_dependencies = ignore_dependencies
self._upgrade_strategy = upgrade_strategy
self.roots = roots
self.user_requested = user_requested
def sort_matches(self, matches):
def _sort_matches(self, matches):
# type: (Sequence[Candidate]) -> Sequence[Candidate]
# The requirement is responsible for returning a sequence of potential
@ -76,28 +74,36 @@ class PipProvider(AbstractProvider):
def _eligible_for_upgrade(name):
# type: (str) -> bool
"""Are upgrades allowed for this project?
This checks the upgrade strategy, and whether the project was one
that the user specified in the command line, in order to decide
whether we should upgrade if there's a newer version available.
(Note that we don't need access to the `--upgrade` flag, because
an upgrade strategy of "to-satisfy-only" means that `--upgrade`
was not specified).
"""
if self._upgrade_strategy == "eager":
return True
elif self._upgrade_strategy == "only-if-needed":
return (name in self.roots)
return (name in self.user_requested)
return False
def keep_installed(c):
# type: (Candidate) -> int
"""Give priority to an installed version?"""
if not is_already_installed(c):
return 0
if _eligible_for_upgrade(c.name):
return 0
return 1
def key(c):
def sort_key(c):
# type: (Candidate) -> Tuple[int, _BaseVersion]
return (keep_installed(c), c.version)
"""Return a sort key for the matches.
return sorted(matches, key=key)
The highest priority should be given to installed candidates that
are not eligible for upgrade. We use the integer value in the first
part of the key to sort these before other candidates.
"""
if c.is_installed and not _eligible_for_upgrade(c.name):
return (1, c.version)
return (0, c.version)
return sorted(matches, key=sort_key)
def get_install_requirement(self, c):
# type: (Candidate) -> Optional[InstallRequirement]
@ -121,7 +127,7 @@ class PipProvider(AbstractProvider):
# type: (Requirement) -> Sequence[Candidate]
constraint = self._constraints.get(requirement.name, SpecifierSet())
matches = requirement.find_matches(constraint)
return self.sort_matches(matches)
return self._sort_matches(matches)
def is_satisfied_by(self, requirement, candidate):
# type: (Requirement, Candidate) -> bool

View File

@ -76,6 +76,10 @@ class SpecifierRequirement(Requirement):
def find_matches(self, constraint):
# type: (SpecifierSet) -> Sequence[Candidate]
# We should only return one candidate per version, but
# iter_found_candidates does that for us, so we don't need
# to do anything special here.
return [
c
for c in self._factory.iter_found_candidates(

View File

@ -69,7 +69,7 @@ class Resolver(BaseResolver):
# type: (List[InstallRequirement], bool) -> RequirementSet
constraints = {} # type: Dict[str, SpecifierSet]
roots = set()
user_requested = set()
requirements = []
for req in root_reqs:
if req.constraint:
@ -82,7 +82,7 @@ class Resolver(BaseResolver):
constraints[name] = req.specifier
else:
if req.is_direct and req.name:
roots.add(canonicalize_name(req.name))
user_requested.add(canonicalize_name(req.name))
requirements.append(
self.factory.make_requirement_from_install_req(req)
)
@ -92,7 +92,7 @@ class Resolver(BaseResolver):
constraints=constraints,
ignore_dependencies=self.ignore_dependencies,
upgrade_strategy=self.upgrade_strategy,
roots=roots,
user_requested=user_requested,
)
reporter = BaseReporter()
resolver = RLResolver(provider, reporter)

View File

@ -66,5 +66,5 @@ def provider(factory):
constraints={},
ignore_dependencies=False,
upgrade_strategy="to-satisfy-only",
roots=set(),
user_requested=set(),
)