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 import six
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
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._vendor.resolvelib import Resolver as RLResolver
|
||||||
|
|
||||||
from pip._internal.exceptions import InstallationError
|
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.req.req_set import RequirementSet
|
||||||
from pip._internal.resolution.base import BaseResolver
|
from pip._internal.resolution.base import BaseResolver
|
||||||
from pip._internal.resolution.resolvelib.provider import PipProvider
|
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.misc import dist_is_editable
|
||||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ class Resolver(BaseResolver):
|
||||||
upgrade_strategy=self.upgrade_strategy,
|
upgrade_strategy=self.upgrade_strategy,
|
||||||
user_requested=user_requested,
|
user_requested=user_requested,
|
||||||
)
|
)
|
||||||
reporter = BaseReporter()
|
reporter = PipReporter()
|
||||||
resolver = RLResolver(provider, reporter)
|
resolver = RLResolver(provider, reporter)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1046,3 +1046,51 @@ def test_new_resolver_prefers_installed_in_upgrade_if_latest(script):
|
||||||
"pkg",
|
"pkg",
|
||||||
)
|
)
|
||||||
assert_installed(script, pkg="2")
|
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