Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log an informational message when backtracking takes multiple rounds on a specific package #9017

Merged
merged 7 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/8975.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Log an informational message when backtracking takes multiple rounds on a specific package.
52 changes: 52 additions & 0 deletions src/pip/_internal/resolution/resolvelib/reporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from collections import defaultdict
from logging import getLogger

from pip._vendor.resolvelib.reporters import BaseReporter

from pip._internal.utils.typing import MYPY_CHECK_RUNNING

if MYPY_CHECK_RUNNING:
from typing import DefaultDict

from .base import Candidate


logger = getLogger(__name__)


class PipReporter(BaseReporter):

def __init__(self):
# type: () -> None
self.backtracks_by_package = defaultdict(int) # type: DefaultDict[str, int]

self._messages_at_backtrack = {
1: (
"pip is looking at multiple versions of this package to determine "
"which version is compatible with other requirements. "
"This could take a while."
),
8: (
"pip is looking at multiple versions of this package to determine "
"which version is compatible with other requirements. "
"This could take a while."
),
pradyunsg marked this conversation as resolved.
Show resolved Hide resolved
13: (
"This is taking longer than usual. You might need to provide the "
"dependency resolver with stricter constraints to reduce runtime."
"If you want to abort this run, you can press Ctrl + C to do so."
"To improve how pip performs, tell us that this happened here: "
pradyunsg marked this conversation as resolved.
Show resolved Hide resolved
"https://pip.pypa.io/surveys/backtracking"
)
}

def backtracking(self, candidate):
# type: (Candidate) -> None
self.backtracks_by_package[candidate.name] += 1

count = self.backtracks_by_package[candidate.name]
if count not in self._messages_at_backtrack:
return

message = self._messages_at_backtrack[count]
logger.info("INFO: %s", message)
5 changes: 3 additions & 2 deletions src/pip/_internal/resolution/resolvelib/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@

from pip._vendor import six
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
from pip._vendor.resolvelib import ResolutionImpossible
from pip._vendor.resolvelib import Resolver as RLResolver

from pip._internal.exceptions import InstallationError
from pip._internal.req.req_install import check_invalid_constraint_type
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import BaseResolver
from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.resolution.resolvelib.reporter import PipReporter
from pip._internal.utils.misc import dist_is_editable
from pip._internal.utils.typing import MYPY_CHECK_RUNNING

Expand Down Expand Up @@ -103,7 +104,7 @@ def resolve(self, root_reqs, check_supported_wheels):
upgrade_strategy=self.upgrade_strategy,
user_requested=user_requested,
)
reporter = BaseReporter()
reporter = PipReporter()
resolver = RLResolver(provider, reporter)

try:
Expand Down
48 changes: 48 additions & 0 deletions tests/functional/test_new_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,3 +1046,51 @@ def test_new_resolver_prefers_installed_in_upgrade_if_latest(script):
"pkg",
)
assert_installed(script, pkg="2")


@pytest.mark.parametrize("N", [2, 10, 20])
def test_new_resolver_presents_messages_when_backtracking_a_lot(script, N):
# Generate a set of wheels that will definitely cause backtracking.
for index in range(1, N+1):
A_version = "{index}.0.0".format(index=index)
B_version = "{index}.0.0".format(index=index)
C_version = "{index_minus_one}.0.0".format(index_minus_one=index - 1)

depends = ["B == " + B_version]
if index != 1:
depends.append("C == " + C_version)

print("A", A_version, "B", B_version, "C", C_version)
create_basic_wheel_for_package(script, "A", A_version, depends=depends)

for index in range(1, N+1):
B_version = "{index}.0.0".format(index=index)
C_version = "{index}.0.0".format(index=index)
depends = ["C == " + C_version]

print("B", B_version, "C", C_version)
create_basic_wheel_for_package(script, "B", B_version, depends=depends)

for index in range(1, N+1):
C_version = "{index}.0.0".format(index=index)
print("C", C_version)
create_basic_wheel_for_package(script, "C", C_version)

# Install A
result = script.pip(
"install",
"--use-feature=2020-resolver",
"--no-cache-dir",
"--no-index",
"--find-links", script.scratch_path,
"A"
)

assert_installed(script, A="1.0.0", B="1.0.0", C="1.0.0")
# These numbers are hard-coded in the code.
if N >= 1:
assert "This could take a while." in result.stdout
if N >= 8:
assert result.stdout.count("This could take a while.") >= 2
if N >= 13:
assert "press Ctrl + C" in result.stdout