@@ -56,9 +56,48 @@ def get_preference(
56
56
information # type: Sequence[Tuple[Requirement, Candidate]]
57
57
):
58
58
# 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 )
59
98
transitive = all (parent is not None for _ , parent in information )
60
99
key = next (iter (candidates )).name if candidates else ""
61
- return (transitive , key )
100
+ return (restrictive , transitive , key )
62
101
63
102
def find_matches (self , requirements ):
64
103
# type: (Sequence[Requirement]) -> Iterable[Candidate]
0 commit comments