Skip to content

Commit

Permalink
Resolve direct and pinned requirements first
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Dec 3, 2020
1 parent 30eeb9c commit 2e13758
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
2 changes: 2 additions & 0 deletions news/9185.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
New resolver: Resolve direct and pinned (``==`` or ``===``) requirements first
to improve resolver performance.
41 changes: 40 additions & 1 deletion src/pip/_internal/resolution/resolvelib/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,48 @@ def get_preference(
information # type: Sequence[Tuple[Requirement, Candidate]]
):
# type: (...) -> Any
"""Produce a sort key for given requirement based on preference.
The lower the return value is, the more preferred this group of
arguments is.
Currently pip considers the followings in order:
* Prefer if any of the known requirements points to an explicit URL.
* If equal, prefer if any requirements contain `===` and `==`.
* If equal, prefer user-specified (non-transitive) requirements.
* If equal, order alphabetically for consistency (helps debuggability).
"""

def _get_restrictive_rating(requirements):
# type: (Iterable[Requirement]) -> int
"""Rate how restrictive a set of requirements are.
``Requirement.get_candidate_lookup()`` returns a 2-tuple for
lookup. The first element is ``Optional[Candidate]`` and the
second ``Optional[InstallRequirement]``.
* If the requirement is an explicit one, the explicitly-required
candidate is returned as the first element.
* If the requirement is based on a PEP 508 specifier, the backing
``InstallRequirement`` is returned as the second element.
We use the first element to check whether there is an explicit
requirement, and the second for equality operator.
"""
lookups = (r.get_candidate_lookup() for r in requirements)
cands, ireqs = zip(*lookups)
if any(cand is not None for cand in cands):
return 0
operators = (ireq.specifier.operator for ireq in ireqs if ireq)
if any(op in ("==", "===") for op in operators):
return 1
return 2

restrictive = _get_restrictive_rating(req for req, _ in information)
transitive = all(parent is not None for _, parent in information)
key = next(iter(candidates)).name if candidates else ""
return (transitive, key)
return (restrictive, transitive, key)

def find_matches(self, requirements):
# type: (Sequence[Requirement]) -> Iterable[Candidate]
Expand Down

0 comments on commit 2e13758

Please sign in to comment.