@@ -56,9 +56,48 @@ def get_preference(
5656 information # type: Sequence[Tuple[Requirement, Candidate]]
5757 ):
5858 # type: (...) -> Any
59+ """Produce a sort key for given requirement based on preference.
60+
61+ The lower the return value is, the more preferred this group of
62+ arguments is.
63+
64+ Currently pip considers the followings in order:
65+
66+ * Prefer if any of the known requirements points to an explicit URL.
67+ * If equal, prefer if any requirements contain `===` and `==`.
68+ * If equal, prefer user-specified (non-transitive) requirements.
69+ * If equal, order alphabetically for consistency (helps debuggability).
70+ """
71+
72+ def _get_restrictive_rating (requirements ):
73+ # type: (Iterable[Requirement]) -> int
74+ """Rate how restrictive a set of requirements are.
75+
76+ ``Requirement.get_candidate_lookup()`` returns a 2-tuple for
77+ lookup. The first element is ``Optional[Candidate]`` and the
78+ second ``Optional[InstallRequirement]``.
79+
80+ * If the requirement is an explicit one, the explicitly-required
81+ candidate is returned as the first element.
82+ * If the requirement is based on a PEP 508 specifier, the backing
83+ ``InstallRequirement`` is returned as the second element.
84+
85+ We use the first element to check whether there is an explicit
86+ requirement, and the second for equality operator.
87+ """
88+ lookups = (r .get_candidate_lookup () for r in requirements )
89+ cands , ireqs = zip (* lookups )
90+ if any (cand is not None for cand in cands ):
91+ return 0
92+ operators = (ireq .specifier .operator for ireq in ireqs if ireq )
93+ if any (op in ("==" , "===" ) for op in operators ):
94+ return 1
95+ return 2
96+
97+ restrictive = _get_restrictive_rating (req for req , _ in information )
5998 transitive = all (parent is not None for _ , parent in information )
6099 key = next (iter (candidates )).name if candidates else ""
61- return (transitive , key )
100+ return (restrictive , transitive , key )
62101
63102 def find_matches (self , requirements ):
64103 # type: (Sequence[Requirement]) -> Iterable[Candidate]
0 commit comments