From 3642589903743be36449f4e6f8d9a8c091e5c594 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 13 Apr 2020 11:16:30 +0100 Subject: [PATCH 1/2] Make message more user friendly when unable to resolve --- .../resolution/resolvelib/requirements.py | 4 +++ .../resolution/resolvelib/resolver.py | 28 ++++++++++++++-- tests/functional/test_new_resolver.py | 33 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/requirements.py b/src/pip/_internal/resolution/resolvelib/requirements.py index 027e36aa61e..952973ae7cc 100644 --- a/src/pip/_internal/resolution/resolvelib/requirements.py +++ b/src/pip/_internal/resolution/resolvelib/requirements.py @@ -79,6 +79,10 @@ def __init__(self, ireq, factory): self._factory = factory self.extras = ireq.req.extras + def __str__(self): + # type: () -> str + return str(self._ireq.req) + def __repr__(self): # type: () -> str return "{class_name}({requirement!r})".format( diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index 5e6408573c6..d2e571f17bf 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -1,9 +1,11 @@ import functools +import logging from pip._vendor.packaging.utils import canonicalize_name -from pip._vendor.resolvelib import BaseReporter +from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible from pip._vendor.resolvelib import Resolver as RLResolver +from pip._internal.exceptions import InstallationError from pip._internal.req.req_set import RequirementSet from pip._internal.resolution.base import BaseResolver from pip._internal.resolution.resolvelib.provider import PipProvider @@ -23,6 +25,9 @@ from pip._internal.resolution.base import InstallRequirementProvider +logger = logging.getLogger(__name__) + + class Resolver(BaseResolver): def __init__( self, @@ -64,7 +69,26 @@ def resolve(self, root_reqs, check_supported_wheels): self.factory.make_requirement_from_install_req(r) for r in root_reqs ] - self._result = resolver.resolve(requirements) + + try: + self._result = resolver.resolve(requirements) + except ResolutionImpossible as exc: + # TODO: This is just an initial version. May need more work. + # Also could do with rewriting to fit better into 80-char + # lines :-( + for req, parent in exc.causes: + logger.critical( + "Could not find a version that satisfies " + + "the requirement " + + str(req) + + ("" if parent is None else " (from {})".format( + parent.name + )) + ) + raise InstallationError( + "No matching distribution found for " + + ", ".join([r.name for r, _ in exc.causes]) + ) req_set = RequirementSet(check_supported_wheels=check_supported_wheels) for candidate in self._result.mapping.values(): diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index 25c726f2574..5d119865e7f 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -155,6 +155,39 @@ def test_new_resolver_installs_extras(script): assert_installed(script, base="0.1.0", dep="0.1.0") +def test_new_resolver_installed_message(script): + create_basic_wheel_for_package(script, "A", "1.0") + result = script.pip( + "install", "--unstable-feature=resolver", + "--no-cache-dir", "--no-index", + "--find-links", script.scratch_path, + "A", + expect_stderr=False, + ) + assert "Successfully installed A-1.0" in result.stdout, str(result) + + +def test_new_resolver_no_dist_message(script): + create_basic_wheel_for_package(script, "A", "1.0") + result = script.pip( + "install", "--unstable-feature=resolver", + "--no-cache-dir", "--no-index", + "--find-links", script.scratch_path, + "B", + expect_error=True, + expect_stderr=True, + ) + + # Full messages from old resolver: + # ERROR: Could not find a version that satisfies the + # requirement xxx (from versions: none) + # ERROR: No matching distribution found for xxx + + assert "Could not find a version that satisfies the requirement B" \ + in result.stderr, str(result) + assert "No matching distribution found for B" in result.stderr, str(result) + + def test_new_resolver_installs_editable(script): create_basic_wheel_for_package( script, From 8c118c8f3a6fd850e49256c51440142526a3d7c6 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 14 Apr 2020 15:18:24 +0100 Subject: [PATCH 2/2] Fix the test to check for canonical name --- tests/functional/test_new_resolver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index 73ceff9aaea..cb384d7a0ac 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -186,7 +186,9 @@ def test_new_resolver_no_dist_message(script): assert "Could not find a version that satisfies the requirement B" \ in result.stderr, str(result) - assert "No matching distribution found for B" in result.stderr, str(result) + # TODO: This reports the canonical name of the project. But the current + # resolver reports the originally specified name (i.e. uppercase B) + assert "No matching distribution found for b" in result.stderr, str(result) def test_new_resolver_installs_editable(script):