Skip to content

Commit

Permalink
Merge pull request #9771 from uranusjr/resolvelib-060
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr authored Apr 12, 2021
2 parents a0f6041 + 6468108 commit e6a65fc
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 97 deletions.
2 changes: 1 addition & 1 deletion news/resolvelib.vendor.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Upgrade vendored resolvelib to 0.5.5.
Upgrade vendored resolvelib to 0.6.0.
62 changes: 42 additions & 20 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 @@ -104,6 +105,9 @@ def __init__(
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 +122,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 +144,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 +196,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 +271,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 +304,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
31 changes: 21 additions & 10 deletions src/pip/_internal/resolution/resolvelib/provider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Union
from typing import (
TYPE_CHECKING,
Dict,
Iterable,
Iterator,
Mapping,
Optional,
Sequence,
Union,
)

from pip._vendor.resolvelib.providers import AbstractProvider

Expand Down Expand Up @@ -134,12 +143,12 @@ def _get_restrictive_rating(requirements):

return (delay_this, restrictive, order, key)

def find_matches(self, requirements):
# type: (Sequence[Requirement]) -> Iterable[Candidate]
if not requirements:
return []
name = requirements[0].project_name

def find_matches(
self,
identifier: str,
requirements: Mapping[str, Iterator[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
) -> Iterable[Candidate]:
def _eligible_for_upgrade(name):
# type: (str) -> bool
"""Are upgrades allowed for this project?
Expand All @@ -159,9 +168,11 @@ def _eligible_for_upgrade(name):
return False

return self._factory.find_candidates(
requirements,
constraint=self._constraints.get(name, Constraint.empty()),
prefers_installed=(not _eligible_for_upgrade(name)),
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
2 changes: 1 addition & 1 deletion src/pip/_vendor/resolvelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"ResolutionTooDeep",
]

__version__ = "0.5.5"
__version__ = "0.6.0"


from .providers import AbstractProvider, AbstractResolver
Expand Down
6 changes: 3 additions & 3 deletions src/pip/_vendor/resolvelib/compat/collections_abc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["Sequence"]
__all__ = ["Mapping", "Sequence"]

try:
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
except ImportError:
from collections import Sequence
from collections import Mapping, Sequence
18 changes: 12 additions & 6 deletions src/pip/_vendor/resolvelib/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,18 @@ def get_preference(self, resolution, candidates, information):
"""
raise NotImplementedError

def find_matches(self, requirements):
"""Find all possible candidates that satisfy the given requirements.
def find_matches(self, identifier, requirements, incompatibilities):
"""Find all possible candidates that satisfy given constraints.
:param identifier: An identifier as returned by ``identify()``. This
identifies the dependency matches of which should be returned.
:param requirements: A mapping of requirements that all returned
candidates must satisfy. Each key is an identifier, and the value
an iterator of requirements for that dependency.
:param incompatibilities: A mapping of known incompatibilities of
each dependency. Each key is an identifier, and the value an
iterator of incompatibilities known to the resolver. All
incompatibilities *must* be excluded from the return value.
This should try to get candidates based on the requirements' types.
For VCS, local, and archive requirements, the one-and-only match is
Expand All @@ -66,10 +76,6 @@ def find_matches(self, requirements):
* An collection of candidates.
* An iterable of candidates. This will be consumed immediately into a
list of candidates.
:param requirements: A collection of requirements which all of the
returned candidates must match. All requirements are guaranteed to
have the same identifier. The collection is never empty.
"""
raise NotImplementedError

Expand Down
9 changes: 7 additions & 2 deletions src/pip/_vendor/resolvelib/providers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ from typing import (
Collection,
Generic,
Iterable,
Iterator,
Mapping,
Optional,
Protocol,
Sequence,
Union,
)

Expand All @@ -31,7 +31,12 @@ class AbstractProvider(Generic[RT, CT, KT]):
candidates: IterableView[CT],
information: Collection[RequirementInformation[RT, CT]],
) -> Preference: ...
def find_matches(self, requirements: Sequence[RT]) -> Matches: ...
def find_matches(
self,
identifier: KT,
requirements: Mapping[KT, Iterator[RT]],
incompatibilities: Mapping[KT, Iterator[CT]],
) -> Matches: ...
def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool: ...
def get_dependencies(self, candidate: CT) -> Iterable[RT]: ...

Expand Down
Loading

0 comments on commit e6a65fc

Please sign in to comment.