Merge pull request #9017 from pradyunsg/backtracking-messaging

This commit is contained in:
Pradyun Gedam 2020-10-28 18:25:29 +05:30 committed by GitHub
commit 00e531a16e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 2 deletions

1
news/8975.feature.rst Normal file
View File

@ -0,0 +1 @@
Log an informational message when backtracking takes multiple rounds on a specific package.

View File

@ -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."
),
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 what happened here: "
"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)

View File

@ -3,7 +3,7 @@ import logging
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
@ -11,6 +11,7 @@ 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
@ -103,7 +104,7 @@ class Resolver(BaseResolver):
upgrade_strategy=self.upgrade_strategy,
user_requested=user_requested,
)
reporter = BaseReporter()
reporter = PipReporter()
resolver = RLResolver(provider, reporter)
try:

View File

@ -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