Skip to content

Commit 00e531a

Browse files
authored
Merge pull request #9017 from pradyunsg/backtracking-messaging
2 parents c4dbc7d + 741b80a commit 00e531a

File tree

4 files changed

+104
-2
lines changed

4 files changed

+104
-2
lines changed

news/8975.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Log an informational message when backtracking takes multiple rounds on a specific package.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from collections import defaultdict
2+
from logging import getLogger
3+
4+
from pip._vendor.resolvelib.reporters import BaseReporter
5+
6+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
7+
8+
if MYPY_CHECK_RUNNING:
9+
from typing import DefaultDict
10+
11+
from .base import Candidate
12+
13+
14+
logger = getLogger(__name__)
15+
16+
17+
class PipReporter(BaseReporter):
18+
19+
def __init__(self):
20+
# type: () -> None
21+
self.backtracks_by_package = defaultdict(int) # type: DefaultDict[str, int]
22+
23+
self._messages_at_backtrack = {
24+
1: (
25+
"pip is looking at multiple versions of this package to determine "
26+
"which version is compatible with other requirements. "
27+
"This could take a while."
28+
),
29+
8: (
30+
"pip is looking at multiple versions of this package to determine "
31+
"which version is compatible with other requirements. "
32+
"This could take a while."
33+
),
34+
13: (
35+
"This is taking longer than usual. You might need to provide the "
36+
"dependency resolver with stricter constraints to reduce runtime."
37+
"If you want to abort this run, you can press Ctrl + C to do so."
38+
"To improve how pip performs, tell us what happened here: "
39+
"https://pip.pypa.io/surveys/backtracking"
40+
)
41+
}
42+
43+
def backtracking(self, candidate):
44+
# type: (Candidate) -> None
45+
self.backtracks_by_package[candidate.name] += 1
46+
47+
count = self.backtracks_by_package[candidate.name]
48+
if count not in self._messages_at_backtrack:
49+
return
50+
51+
message = self._messages_at_backtrack[count]
52+
logger.info("INFO: %s", message)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
from pip._vendor import six
55
from pip._vendor.packaging.utils import canonicalize_name
6-
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
6+
from pip._vendor.resolvelib import ResolutionImpossible
77
from pip._vendor.resolvelib import Resolver as RLResolver
88

99
from pip._internal.exceptions import InstallationError
1010
from pip._internal.req.req_install import check_invalid_constraint_type
1111
from pip._internal.req.req_set import RequirementSet
1212
from pip._internal.resolution.base import BaseResolver
1313
from pip._internal.resolution.resolvelib.provider import PipProvider
14+
from pip._internal.resolution.resolvelib.reporter import PipReporter
1415
from pip._internal.utils.misc import dist_is_editable
1516
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
1617

@@ -103,7 +104,7 @@ def resolve(self, root_reqs, check_supported_wheels):
103104
upgrade_strategy=self.upgrade_strategy,
104105
user_requested=user_requested,
105106
)
106-
reporter = BaseReporter()
107+
reporter = PipReporter()
107108
resolver = RLResolver(provider, reporter)
108109

109110
try:

tests/functional/test_new_resolver.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,3 +1046,51 @@ def test_new_resolver_prefers_installed_in_upgrade_if_latest(script):
10461046
"pkg",
10471047
)
10481048
assert_installed(script, pkg="2")
1049+
1050+
1051+
@pytest.mark.parametrize("N", [2, 10, 20])
1052+
def test_new_resolver_presents_messages_when_backtracking_a_lot(script, N):
1053+
# Generate a set of wheels that will definitely cause backtracking.
1054+
for index in range(1, N+1):
1055+
A_version = "{index}.0.0".format(index=index)
1056+
B_version = "{index}.0.0".format(index=index)
1057+
C_version = "{index_minus_one}.0.0".format(index_minus_one=index - 1)
1058+
1059+
depends = ["B == " + B_version]
1060+
if index != 1:
1061+
depends.append("C == " + C_version)
1062+
1063+
print("A", A_version, "B", B_version, "C", C_version)
1064+
create_basic_wheel_for_package(script, "A", A_version, depends=depends)
1065+
1066+
for index in range(1, N+1):
1067+
B_version = "{index}.0.0".format(index=index)
1068+
C_version = "{index}.0.0".format(index=index)
1069+
depends = ["C == " + C_version]
1070+
1071+
print("B", B_version, "C", C_version)
1072+
create_basic_wheel_for_package(script, "B", B_version, depends=depends)
1073+
1074+
for index in range(1, N+1):
1075+
C_version = "{index}.0.0".format(index=index)
1076+
print("C", C_version)
1077+
create_basic_wheel_for_package(script, "C", C_version)
1078+
1079+
# Install A
1080+
result = script.pip(
1081+
"install",
1082+
"--use-feature=2020-resolver",
1083+
"--no-cache-dir",
1084+
"--no-index",
1085+
"--find-links", script.scratch_path,
1086+
"A"
1087+
)
1088+
1089+
assert_installed(script, A="1.0.0", B="1.0.0", C="1.0.0")
1090+
# These numbers are hard-coded in the code.
1091+
if N >= 1:
1092+
assert "This could take a while." in result.stdout
1093+
if N >= 8:
1094+
assert result.stdout.count("This could take a while.") >= 2
1095+
if N >= 13:
1096+
assert "press Ctrl + C" in result.stdout

0 commit comments

Comments
 (0)