Skip to content

Commit 2d2ad3c

Browse files
committed
Prefer requirements with upper bounds
1 parent 873fdf7 commit 2d2ad3c

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

docs/html/topics/more-dependency-resolution.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,11 @@ follows:
168168
* Prefer if any of the known requirements is "direct", e.g. points to an
169169
explicit URL.
170170
* If equal, prefer if any requirement is "pinned", i.e. contains
171-
operator ``===`` or ``==``.
171+
operator ``===`` or ``==`` without a wildcard.
172+
* Prefer if any requirement is "upper-bounded", operators that do
173+
not allow all future versions, e.g. ``<``, ``<=``, ``~=``, and
174+
``==`` with a wildcard.
172175
* Order user-specified requirements by the order they are specified.
173176
* If equal, prefers "non-free" requirements, i.e. contains at least one
174-
operator, such as ``>=`` or ``<``.
177+
operator, such as ``>=`` or ``!=``.
175178
* If equal, order alphabetically for consistency (helps debuggability).

src/pip/_internal/resolution/resolvelib/provider.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,13 @@ def get_preference(
164164
* Prefer if any of the known requirements is "direct", e.g. points to an
165165
explicit URL.
166166
* If equal, prefer if any requirement is "pinned", i.e. contains
167-
operator ``===`` or ``==``.
167+
operator ``===`` or ``==`` without a wildcard.
168+
* Prefer if any requirement is "upper-bounded", operators that do
169+
not allow all future versions, e.g. ``<``, ``<=``, ``~=``, and
170+
``==`` with a wildcard.
168171
* Order user-specified requirements by the order they are specified.
169172
* If equal, prefers "non-free" requirements, i.e. contains at least one
170-
operator, such as ``>=`` or ``<``.
173+
operator, such as ``>=`` or ``!=``.
171174
* If equal, order alphabetically for consistency (helps debuggability).
172175
"""
173176
try:
@@ -193,12 +196,17 @@ def get_preference(
193196

194197
direct = candidate is not None
195198
pinned = any(((op[:2] == "==") and ("*" not in ver)) for op, ver in operators)
199+
upper_bounded = any(
200+
((op in ("<", "<=", "~=")) or (op == "==" and "*" in ver))
201+
for op, ver in operators
202+
)
196203
unfree = bool(operators)
197204
requested_order = self._user_requested.get(identifier, math.inf)
198205

199206
return (
200207
not direct,
201208
not pinned,
209+
not upper_bounded,
202210
requested_order,
203211
not unfree,
204212
identifier,

tests/unit/resolution_resolvelib/test_provider.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,47 +42,79 @@ def build_req_info(
4242
{"pinned-package": [build_req_info("pinned-package==1.0")]},
4343
[],
4444
{},
45-
(False, False, math.inf, False, "pinned-package"),
45+
(False, False, True, math.inf, False, "pinned-package"),
4646
),
4747
# Star-specified package, i.e. with "*"
4848
(
4949
"star-specified-package",
5050
{"star-specified-package": [build_req_info("star-specified-package==1.*")]},
5151
[],
5252
{},
53-
(False, True, math.inf, False, "star-specified-package"),
53+
(False, True, False, math.inf, False, "star-specified-package"),
5454
),
5555
# Package that caused backtracking
5656
(
5757
"backtrack-package",
5858
{"backtrack-package": [build_req_info("backtrack-package")]},
5959
[build_req_info("backtrack-package")],
6060
{},
61-
(False, True, math.inf, True, "backtrack-package"),
61+
(False, True, True, math.inf, True, "backtrack-package"),
6262
),
6363
# Root package requested by user
6464
(
6565
"root-package",
6666
{"root-package": [build_req_info("root-package")]},
6767
[],
6868
{"root-package": 1},
69-
(False, True, 1, True, "root-package"),
69+
(False, True, True, 1, True, "root-package"),
7070
),
7171
# Unfree package (with specifier operator)
7272
(
7373
"unfree-package",
74-
{"unfree-package": [build_req_info("unfree-package<1")]},
74+
{"unfree-package": [build_req_info("unfree-package!=1")]},
7575
[],
7676
{},
77-
(False, True, math.inf, False, "unfree-package"),
77+
(False, True, True, math.inf, False, "unfree-package"),
7878
),
7979
# Free package (no operator)
8080
(
8181
"free-package",
8282
{"free-package": [build_req_info("free-package")]},
8383
[],
8484
{},
85-
(False, True, math.inf, True, "free-package"),
85+
(False, True, True, math.inf, True, "free-package"),
86+
),
87+
# Upper bounded with <= operator
88+
(
89+
"upper-bound-lte-package",
90+
{
91+
"upper-bound-lte-package": [
92+
build_req_info("upper-bound-lte-package<=2.0")
93+
]
94+
},
95+
[],
96+
{},
97+
(False, True, False, math.inf, False, "upper-bound-lte-package"),
98+
),
99+
# Upper bounded with ~= operator
100+
(
101+
"upper-bound-compatible-package",
102+
{
103+
"upper-bound-compatible-package": [
104+
build_req_info("upper-bound-compatible-package~=1.0")
105+
]
106+
},
107+
[],
108+
{},
109+
(False, True, False, math.inf, False, "upper-bound-compatible-package"),
110+
),
111+
# Not upper bounded, using only >= operator
112+
(
113+
"lower-bound-package",
114+
{"lower-bound-package": [build_req_info("lower-bound-package>=1.0")]},
115+
[],
116+
{},
117+
(False, True, True, math.inf, False, "lower-bound-package"),
86118
),
87119
],
88120
)

0 commit comments

Comments
 (0)