mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #9017 from pradyunsg/backtracking-messaging
This commit is contained in:
commit
00e531a16e
4 changed files with 104 additions and 2 deletions
1
news/8975.feature.rst
Normal file
1
news/8975.feature.rst
Normal file
|
@ -0,0 +1 @@
|
|||
Log an informational message when backtracking takes multiple rounds on a specific package.
|
52
src/pip/_internal/resolution/resolvelib/reporter.py
Normal file
52
src/pip/_internal/resolution/resolvelib/reporter.py
Normal 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)
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue