Skip to content

Commit

Permalink
Take responsibility to remove incompatibilities
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Apr 4, 2021
1 parent 778778c commit 8b3ef88
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 35 deletions.
66 changes: 43 additions & 23 deletions src/pip/_internal/resolution/resolvelib/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
Iterable,
Iterator,
List,
Mapping,
Optional,
Sequence,
Set,
Expand Down Expand Up @@ -101,9 +102,10 @@ def __init__(
self._build_failures = {} # type: Cache[InstallationError]
self._link_candidate_cache = {} # type: Cache[LinkCandidate]
self._editable_candidate_cache = {} # type: Cache[EditableCandidate]
self._installed_candidate_cache = (
{}
) # type: Dict[str, AlreadyInstalledCandidate]
self._installed_candidate_cache = {
} # type: Dict[str, AlreadyInstalledCandidate]
self._extras_candidate_cache = {
} # type: Dict[Tuple[int, FrozenSet[str]], ExtrasCandidate]

if not ignore_installed:
self._installed_dists = {
Expand All @@ -118,6 +120,16 @@ def force_reinstall(self):
# type: () -> bool
return self._force_reinstall

def _make_extras_candidate(self, base, extras):
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
cache_key = (id(base), extras)
try:
candidate = self._extras_candidate_cache[cache_key]
except KeyError:
candidate = ExtrasCandidate(base, extras)
self._extras_candidate_cache[cache_key] = candidate
return candidate

def _make_candidate_from_dist(
self,
dist, # type: Distribution
Expand All @@ -130,9 +142,9 @@ def _make_candidate_from_dist(
except KeyError:
base = AlreadyInstalledCandidate(dist, template, factory=self)
self._installed_candidate_cache[dist.key] = base
if extras:
return ExtrasCandidate(base, extras)
return base
if not extras:
return base
return self._make_extras_candidate(base, extras)

def _make_candidate_from_link(
self,
Expand Down Expand Up @@ -182,18 +194,18 @@ def _make_candidate_from_link(
return None
base = self._link_candidate_cache[link]

if extras:
return ExtrasCandidate(base, extras)
return base
if not extras:
return base
return self._make_extras_candidate(base, extras)

def _iter_found_candidates(
self,
ireqs, # type: Sequence[InstallRequirement]
specifier, # type: SpecifierSet
hashes, # type: Hashes
prefers_installed, # type: bool
):
# type: (...) -> Iterable[Candidate]
ireqs: Sequence[InstallRequirement],
specifier: SpecifierSet,
hashes: Hashes,
prefers_installed: bool,
incompatible_ids: Set[int],
) -> Iterable[Candidate]:
if not ireqs:
return ()

Expand Down Expand Up @@ -257,20 +269,27 @@ def iter_index_candidate_infos():
iter_index_candidate_infos,
installed_candidate,
prefers_installed,
incompatible_ids,
)

def find_candidates(
self,
requirements, # type: Sequence[Requirement]
constraint, # type: Constraint
prefers_installed, # type: bool
):
# type: (...) -> Iterable[Candidate]
identifier: str,
requirements: Mapping[str, Iterator[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
constraint: Constraint,
prefers_installed: bool,
) -> Iterable[Candidate]:

# Since we cache all the candidates, incompatibility identification
# can be made quicker by comparing only the id() values.
incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}

explicit_candidates = set() # type: Set[Candidate]
ireqs = [] # type: List[InstallRequirement]
for req in requirements:
for req in requirements[identifier]:
cand, ireq = req.get_candidate_lookup()
if cand is not None:
if cand is not None and id(cand) not in incompat_ids:
explicit_candidates.add(cand)
if ireq is not None:
ireqs.append(ireq)
Expand All @@ -283,13 +302,14 @@ def find_candidates(
constraint.specifier,
constraint.hashes,
prefers_installed,
incompat_ids,
)

return (
c
for c in explicit_candidates
if constraint.is_satisfied_by(c)
and all(req.is_satisfied_by(c) for req in requirements)
and all(req.is_satisfied_by(c) for req in requirements[identifier])
)

def make_requirement_from_install_req(self, ireq, requested_extras):
Expand Down
18 changes: 11 additions & 7 deletions src/pip/_internal/resolution/resolvelib/found_candidates.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ class FoundCandidates(collections_abc.Sequence):

def __init__(
self,
get_infos, # type: Callable[[], Iterator[IndexCandidateInfo]]
installed, # type: Optional[Candidate]
prefers_installed, # type: bool
get_infos: Callable[[], Iterator[IndexCandidateInfo]],
installed: Optional[Candidate],
prefers_installed: bool,
incompatible_ids: Set[int],
):
self._get_infos = get_infos
self._installed = installed
self._prefers_installed = prefers_installed
self._incompatible_ids = incompatible_ids

def __getitem__(self, index):
# type: (int) -> Candidate
Expand All @@ -119,10 +121,12 @@ def __iter__(self):
# type: () -> Iterator[Candidate]
infos = self._get_infos()
if not self._installed:
return _iter_built(infos)
if self._prefers_installed:
return _iter_built_with_prepended(self._installed, infos)
return _iter_built_with_inserted(self._installed, infos)
iterator = _iter_built(infos)
elif self._prefers_installed:
iterator = _iter_built_with_prepended(self._installed, infos)
else:
iterator = _iter_built_with_inserted(self._installed, infos)
return (c for c in iterator if id(c) not in self._incompatible_ids)

def __len__(self):
# type: () -> int
Expand Down
8 changes: 3 additions & 5 deletions src/pip/_internal/resolution/resolvelib/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,6 @@ def find_matches(
requirements: Mapping[str, Iterator[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
) -> Iterable[Candidate]:
try:
current_requirements = requirements[identifier]
except KeyError:
return []

def _eligible_for_upgrade(name):
# type: (str) -> bool
Expand All @@ -173,9 +169,11 @@ def _eligible_for_upgrade(name):
return False

return self._factory.find_candidates(
list(current_requirements),
identifier=identifier,
requirements=requirements,
constraint=self._constraints.get(identifier, Constraint.empty()),
prefers_installed=(not _eligible_for_upgrade(identifier)),
incompatibilities=incompatibilities,
)

def is_satisfied_by(self, requirement, candidate):
Expand Down

0 comments on commit 8b3ef88

Please sign in to comment.