This commit is contained in:
Stephen Finucane 2023-11-13 16:00:34 +02:00 committed by GitHub
commit a0dea514be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 62 additions and 6 deletions

3
news/7839.feature.rst Normal file
View File

@ -0,0 +1,3 @@
Add the ``--ignore-constraint`` option to ``pip install``, ``pip download``
and ``pip wheel`` commands to ignore an individual constraint from a
constraints file.

View File

@ -415,6 +415,19 @@ def constraints() -> Option:
)
def ignored_constraints() -> Option:
return Option(
"--ignore-constraint",
dest="ignored_constraints",
action="append",
default=[],
metavar="package",
help="Ignore constraints for given package. This is commonly used "
"during development of a package when using a common constraints "
"file. This option be used multiple times.",
)
def requirements() -> Option:
return Option(
"-r",

View File

@ -363,10 +363,13 @@ class RequirementCommand(IndexGroupCommand):
ignore_requires_python=ignore_requires_python,
force_reinstall=force_reinstall,
upgrade_strategy=upgrade_strategy,
ignored_constraints=options.ignored_constraints,
py_version_info=py_version_info,
)
import pip._internal.resolution.legacy.resolver
# we intentionally don't pass ignored_constraints to this since the
# resolver is deprecated
return pip._internal.resolution.legacy.resolver.Resolver(
preparer=preparer,
finder=finder,

View File

@ -37,6 +37,7 @@ class DownloadCommand(RequirementCommand):
def add_options(self) -> None:
self.cmd_opts.add_option(cmdoptions.constraints())
self.cmd_opts.add_option(cmdoptions.ignored_constraints())
self.cmd_opts.add_option(cmdoptions.requirements())
self.cmd_opts.add_option(cmdoptions.no_deps())
self.cmd_opts.add_option(cmdoptions.global_options())

View File

@ -72,6 +72,7 @@ class InstallCommand(RequirementCommand):
def add_options(self) -> None:
self.cmd_opts.add_option(cmdoptions.requirements())
self.cmd_opts.add_option(cmdoptions.constraints())
self.cmd_opts.add_option(cmdoptions.ignored_constraints())
self.cmd_opts.add_option(cmdoptions.no_deps())
self.cmd_opts.add_option(cmdoptions.pre())

View File

@ -61,6 +61,7 @@ class WheelCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.no_use_pep517())
self.cmd_opts.add_option(cmdoptions.check_build_deps())
self.cmd_opts.add_option(cmdoptions.constraints())
self.cmd_opts.add_option(cmdoptions.ignored_constraints())
self.cmd_opts.add_option(cmdoptions.editable())
self.cmd_opts.add_option(cmdoptions.requirements())
self.cmd_opts.add_option(cmdoptions.src())

View File

@ -84,6 +84,8 @@ class PipProvider(_ProviderBase):
:params upgrade_strategy: The user-specified upgrade strategy.
:params user_requested: A set of canonicalized package names that the user
supplied for pip to install/upgrade.
:params ignored_constraints: A list of canonicalized package names that the
user has asked us to ignore constraints for.
"""
def __init__(
@ -93,12 +95,14 @@ class PipProvider(_ProviderBase):
ignore_dependencies: bool,
upgrade_strategy: str,
user_requested: Dict[str, int],
ignored_constraints: Sequence[str],
) -> None:
self._factory = factory
self._constraints = constraints
self._ignore_dependencies = ignore_dependencies
self._upgrade_strategy = upgrade_strategy
self._user_requested = user_requested
self._ignored_constraints = ignored_constraints
self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf)
def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
@ -223,11 +227,15 @@ class PipProvider(_ProviderBase):
return user_order is not None
return False
constraint = _get_with_identifier(
self._constraints,
identifier,
default=Constraint.empty(),
)
if identifier not in self._ignored_constraints:
constraint = _get_with_identifier(
self._constraints,
identifier,
default=Constraint.empty(),
)
else:
constraint = Constraint.empty()
return self._factory.find_candidates(
identifier=identifier,
requirements=requirements,

View File

@ -2,7 +2,7 @@ import contextlib
import functools
import logging
import os
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Set, Tuple, cast
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
@ -50,6 +50,7 @@ class Resolver(BaseResolver):
ignore_requires_python: bool,
force_reinstall: bool,
upgrade_strategy: str,
ignored_constraints: Sequence[str],
py_version_info: Optional[Tuple[int, ...]] = None,
):
super().__init__()
@ -68,6 +69,7 @@ class Resolver(BaseResolver):
)
self.ignore_dependencies = ignore_dependencies
self.upgrade_strategy = upgrade_strategy
self.ignored_constraints = ignored_constraints
self._result: Optional[Result] = None
def resolve(
@ -80,6 +82,7 @@ class Resolver(BaseResolver):
ignore_dependencies=self.ignore_dependencies,
upgrade_strategy=self.upgrade_strategy,
user_requested=collected.user_requested,
ignored_constraints=self.ignored_constraints,
)
if "PIP_RESOLVER_DEBUG" in os.environ:
reporter: BaseReporter = PipDebuggingReporter()

View File

@ -681,6 +681,26 @@ def test_new_resolver_constraints(
script.assert_not_installed("constraint_only")
def test_new_resolver_constraint_ignored(script: PipTestEnvironment) -> None:
"We can ignore constraints. Useful when hacking on a constrained package."
create_basic_wheel_for_package(script, "pkg", "1.1.dev1")
constraints_file = script.scratch_path / "constraints.txt"
constraints_file.write_text("pkg==1.0")
script.pip(
"install",
"--no-cache-dir",
"--no-index",
"--find-links",
script.scratch_path,
"-c",
constraints_file,
"--ignore-constraint",
"pkg",
"pkg",
)
script.assert_installed(pkg="1.1.dev1")
def test_new_resolver_constraint_no_specifier(script: PipTestEnvironment) -> None:
"It's allowed (but useless...) for a constraint to have no specifier"
create_basic_wheel_for_package(script, "pkg", "1.0")

View File

@ -75,4 +75,5 @@ def provider(factory: Factory) -> Iterator[PipProvider]:
ignore_dependencies=False,
upgrade_strategy="to-satisfy-only",
user_requested={},
ignored_constraints=[],
)

View File

@ -36,6 +36,7 @@ def test_provider_known_depths(factory: Factory) -> None:
ignore_dependencies=False,
upgrade_strategy="to-satisfy-only",
user_requested={root_requirement_name: 0},
ignored_constraints=[],
)
root_requirement_information = build_requirement_information(

View File

@ -29,6 +29,7 @@ def resolver(preparer: RequirementPreparer, finder: PackageFinder) -> Resolver:
ignore_requires_python=False,
force_reinstall=False,
upgrade_strategy="to-satisfy-only",
ignored_constraints=[],
)
return resolver