diff --git a/news/8758.bugfix b/news/8758.bugfix new file mode 100644 index 000000000..9f44b7e47 --- /dev/null +++ b/news/8758.bugfix @@ -0,0 +1,2 @@ +New resolver: Correctly respect ``Requires-Python`` metadata to reject +incompatible packages in ``--no-deps`` mode. diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 46cc7e7a2..8b39d2dcb 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -261,31 +261,25 @@ class _InstallRequirementBackedCandidate(Candidate): self._fetch_metadata() return self._dist - def _get_requires_python_specifier(self): - # type: () -> Optional[SpecifierSet] + def _get_requires_python_dependency(self): + # type: () -> Optional[Requirement] requires_python = get_requires_python(self.dist) if requires_python is None: return None try: spec = SpecifierSet(requires_python) except InvalidSpecifier as e: - logger.warning( - "Package %r has an invalid Requires-Python: %s", self.name, e, - ) + message = "Package %r has an invalid Requires-Python: %s" + logger.warning(message, self.name, e) return None - return spec + return self._factory.make_requires_python_requirement(spec) def iter_dependencies(self, with_requires): # type: (bool) -> Iterable[Optional[Requirement]] - if not with_requires: - return - for r in self.dist.requires(): + requires = self.dist.requires() if with_requires else () + for r in requires: yield self._factory.make_requirement_from_spec(str(r), self._ireq) - python_dep = self._factory.make_requires_python_requirement( - self._get_requires_python_specifier(), - ) - if python_dep: - yield python_dep + yield self._get_requires_python_dependency() def get_install_requirement(self): # type: () -> Optional[InstallRequirement] diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index 46e32ddd3..1dab8d470 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -988,3 +988,35 @@ def test_new_resolver_local_and_req(script): source_dir, "pkg!=0.1.0", expect_error=True, ) + + +def test_new_resolver_no_deps_checks_requires_python(script): + create_basic_wheel_for_package( + script, + "base", + "0.1.0", + depends=["dep"], + requires_python="<2", # Something that always fails. + ) + create_basic_wheel_for_package( + script, + "dep", + "0.2.0", + ) + + result = script.pip( + "install", + "--use-feature=2020-resolver", + "--no-cache-dir", + "--no-index", + "--no-deps", + "--find-links", script.scratch_path, + "base", + expect_error=True, + ) + + message = ( + "Package 'base' requires a different Python: " + "{}.{}.{} not in '<2'".format(*sys.version_info[:3]) + ) + assert message in result.stderr