diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index be0729e3994..2de8c88e50e 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -402,8 +402,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 = f"{req} (from {parent.name})" + logger.critical( + "Could not find a version that satisfies the " + "requirement %s", req_disp, + ) + return DistributionNotFound( + f"No matching distribution found for {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" @@ -423,17 +441,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 = f'{req} (from {parent.name})' - logger.critical( - "Could not find a version that satisfies the requirement %s", - req_disp, - ) - return DistributionNotFound( - f'No matching distribution found for {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. @@ -473,7 +482,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( @@ -483,6 +496,9 @@ def describe_trigger(parent): else: msg = msg + "The user requested " msg = msg + req.format_for_error() + for key in relevant_constraints: + spec = constraints[key].specifier + msg += f"\n The user requested (constraint) {key}{spec}" 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 d30d696fc46..75a34ed0c35 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 9c35aee8320..1a44e6a13b5 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 4d2acbb23b4..16f9f4f4216 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -687,7 +687,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"), ], )