diff --git a/news/9185.feature.rst b/news/9185.feature.rst new file mode 100644 index 00000000000..a9d9ae7187c --- /dev/null +++ b/news/9185.feature.rst @@ -0,0 +1,2 @@ +New resolver: Resolve direct and pinned (``==`` or ``===``) requirements first +to improve resolver performance. diff --git a/src/pip/_internal/resolution/resolvelib/provider.py b/src/pip/_internal/resolution/resolvelib/provider.py index c0e6b60d90a..9324b03a1c9 100644 --- a/src/pip/_internal/resolution/resolvelib/provider.py +++ b/src/pip/_internal/resolution/resolvelib/provider.py @@ -56,9 +56,33 @@ 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 + 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 + if any(ireq.is_pinned for ireq in ireqs if ireq is not None): + 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]