diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 35345c5f0a1..ab22837687c 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -403,8 +403,26 @@ def _report_requires_python_error( ) return UnsupportedPythonVersion(message) - def get_installation_error(self, e): - # type: (ResolutionImpossible) -> InstallationError + def _report_single_requirement_conflict(self, req, parent): + # type: (Requirement, Candidate) -> DistributionNotFound + if parent is None: + req_disp = str(req) + else: + req_disp = '{} (from {})'.format(req, parent.name) + logger.critical( + "Could not find a version that satisfies the " + "requirement %s", req_disp, + ) + return DistributionNotFound( + 'No matching distribution found for {}'.format(req) + ) + + def get_installation_error( + self, + e, # type: ResolutionImpossible + constraints, # type: Dict[str, Constraint] + ): + # type: (...) -> InstallationError assert e.causes, "Installation error reported with no cause" @@ -424,17 +442,8 @@ def get_installation_error(self, e): # satisfied. We just report that case. if len(e.causes) == 1: req, parent = e.causes[0] - if parent is None: - req_disp = str(req) - else: - req_disp = '{} (from {})'.format(req, parent.name) - logger.critical( - "Could not find a version that satisfies the requirement %s", - req_disp, - ) - return DistributionNotFound( - 'No matching distribution found for {}'.format(req) - ) + if req.name not in constraints: + return self._report_single_requirement_conflict(req, parent) # OK, we now have a list of requirements that can't all be # satisfied at once. @@ -474,7 +483,11 @@ def describe_trigger(parent): "have conflicting dependencies.".format(info) logger.critical(msg) msg = "\nThe conflict is caused by:" + + relevant_constraints = set() for req, parent in e.causes: + if req.name in constraints: + relevant_constraints.add(req.name) msg = msg + "\n " if parent: msg = msg + "{} {} depends on ".format( @@ -484,6 +497,10 @@ def describe_trigger(parent): else: msg = msg + "The user requested " msg = msg + req.format_for_error() + for key in relevant_constraints: + msg += "\n The user requested (constraint) {}{}".format( + key, constraints[key].specifier, + ) msg = msg + "\n\n" + \ "To fix this you could try to:\n" + \ diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index 30b860f6c48..6f3d7b454d6 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -123,7 +123,7 @@ def resolve(self, root_reqs, check_supported_wheels): ) except ResolutionImpossible as e: - error = self.factory.get_installation_error(e) + error = self.factory.get_installation_error(e, constraints) six.raise_from(error, e) req_set = RequirementSet(check_supported_wheels=check_supported_wheels) diff --git a/tests/functional/test_install_reqs.py b/tests/functional/test_install_reqs.py index c5985243b60..a4ea422beb6 100644 --- a/tests/functional/test_install_reqs.py +++ b/tests/functional/test_install_reqs.py @@ -358,7 +358,7 @@ def test_constraints_local_editable_install_causes_error( assert 'Could not satisfy constraints' in result.stderr, str(result) else: # Because singlemodule only has 0.0.1 available. - assert 'No matching distribution found' in result.stderr, str(result) + assert 'Cannot install singlemodule 0.0.1' in result.stderr, str(result) @pytest.mark.network @@ -387,7 +387,7 @@ def test_constraints_local_install_causes_error( assert 'Could not satisfy constraints' in result.stderr, str(result) else: # Because singlemodule only has 0.0.1 available. - assert 'No matching distribution found' in result.stderr, str(result) + assert 'Cannot install singlemodule 0.0.1' in result.stderr, str(result) def test_constraints_constrain_to_local_editable( diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index 00d82fb95ee..9a6b38552b5 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -715,7 +715,7 @@ def test_new_resolver_constraint_on_dependency(script): @pytest.mark.parametrize( "constraint_version, expect_error, message", [ - ("1.0", True, "ERROR: No matching distribution found for foo 2.0"), + ("1.0", True, "Cannot install foo 2.0"), ("2.0", False, "Successfully installed foo-2.0"), ], )