Tidy up handling of unexpected forms of constraint

This commit is contained in:
Paul Moore 2020-05-14 16:56:55 +01:00
parent d911a9fcb8
commit 6fcaf49cb0
2 changed files with 69 additions and 5 deletions

View File

@ -2,6 +2,7 @@ import functools
import logging
from pip._vendor import six
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
from pip._vendor.resolvelib import Resolver as RLResolver
@ -17,7 +18,6 @@ from .factory import Factory
if MYPY_CHECK_RUNNING:
from typing import Dict, List, Optional, Set, Tuple
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.resolvelib.resolvers import Result
from pip._vendor.resolvelib.structs import Graph
@ -74,13 +74,29 @@ class Resolver(BaseResolver):
requirements = []
for req in root_reqs:
if req.constraint:
assert req.name
assert req.specifier
# TODO: Add warnings to accompany these errors, explaining
# that these were undocumented behaviour of the old resolver
# and will be removed in the new resolver. We need to consider
# how we remember to remove these warnings when the new
# resolver becomes the default...
if not req.name:
raise InstallationError(
"Unnamed requirements are not allowed as constraints"
)
if req.link:
raise InstallationError(
"Links are not allowed as constraints"
)
if req.extras:
raise InstallationError(
"Constraints cannot have extras"
)
specifier = req.specifier or SpecifierSet()
name = canonicalize_name(req.name)
if name in constraints:
constraints[name] = constraints[name] & req.specifier
constraints[name] = constraints[name] & specifier
else:
constraints[name] = req.specifier
constraints[name] = specifier
else:
requirements.append(
self.factory.make_requirement_from_install_req(req)

View File

@ -548,6 +548,54 @@ def test_new_resolver_constraints(script):
assert_not_installed(script, "constraint_only")
def test_new_resolver_constraint_no_specifier(script):
"It's allowed (but useless...) for a constraint to have no specifier"
create_basic_wheel_for_package(script, "pkg", "1.0")
constraints_file = script.scratch_path / "constraints.txt"
constraints_file.write_text("pkg")
script.pip(
"install", "--unstable-feature=resolver",
"--no-cache-dir", "--no-index",
"--find-links", script.scratch_path,
"-c", constraints_file,
"pkg"
)
assert_installed(script, pkg="1.0")
@pytest.mark.parametrize(
"constraint, error",
[
(
"dist.zip",
"Unnamed requirements are not allowed as constraints",
),
(
"req @ https://example.com/dist.zip",
"Links are not allowed as constraints",
),
(
"pkg[extra]",
"Constraints cannot have extras",
),
],
)
def test_new_resolver_constraint_reject_invalid(script, constraint, error):
create_basic_wheel_for_package(script, "pkg", "1.0")
constraints_file = script.scratch_path / "constraints.txt"
constraints_file.write_text(constraint)
result = script.pip(
"install", "--unstable-feature=resolver",
"--no-cache-dir", "--no-index",
"--find-links", script.scratch_path,
"-c", constraints_file,
"pkg",
expect_error=True,
expect_stderr=True,
)
assert error in result.stderr, str(result)
def test_new_resolver_constraint_on_dependency(script):
create_basic_wheel_for_package(script, "base", "1.0", depends=["dep"])
create_basic_wheel_for_package(script, "dep", "1.0")