Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option for minimum version selection #11336

Closed
wants to merge 12 commits into from
Prev Previous commit
Next Next commit
Try warning prompt
  • Loading branch information
davegaeddert committed Jul 28, 2022
commit 838eedbc3d1a7b3701b3a3f7466b96eb9198a94b
39 changes: 32 additions & 7 deletions src/pip/_internal/resolution/resolvelib/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from pip._internal.resolution.base import InstallRequirementProvider
from pip._internal.utils.compatibility_tags import get_supported
from pip._internal.utils.hashes import Hashes
from pip._internal.utils.misc import ask
from pip._internal.utils.packaging import get_requirement
from pip._internal.utils.virtualenv import running_under_virtualenv

Expand Down Expand Up @@ -300,23 +301,47 @@ def is_pinned(specifier: SpecifierSet) -> bool:
return True
return False

pinned = is_pinned(specifier)
def has_lower_bound(specifier: SpecifierSet) -> bool:
for sp in specifier:
if sp.operator in (">", ">=", "~=", "=="):
return True
return False

def should_apply_strategy(name: str) -> bool:
return name not in ("pip", "setuptools", "wheel")

def apply_icans_strategy(name: str, icans: List[IndexCandidateInfo], strategy: str) -> List[IndexCandidateInfo]:
def apply_strategy_to_icans(strategy: str, icans: List[IndexCandidateInfo]) -> List[IndexCandidateInfo]:
if strategy == "prefer-max":
# PackageFinder returns earlier versions first, so we reverse.
return reversed(icans)

if name in ("pip", "setuptools", "wheels"):
# We always want the "max" behavior on these.
return reversed(icans)

if strategy == "prefer-min":
return icans

raise ValueError(f"Unknown strategy: {strategy}")

icans = apply_icans_strategy(name, icans, strategy)
def check_strategy(strategy: str, specifier: SpecifierSet) -> bool:
if strategy == "prefer-min" and not has_lower_bound(specifier):
raise InstallationError(f"No lower bound for {name}")

if should_apply_strategy(name):
try:
check_strategy(strategy, specifier)
except InstallationError as e:
# Resolver will process this more than once, so we need to store the error
if (name, specifier) not in self._build_failures:
logger.warning(f"{name}{specifier} is missing a lower bound.")
if ask(f"Proceed (Y/n)? ", ("y", "n", "")) != "n":
self._build_failures[(name, specifier)] = e
else:
raise e

icans = apply_strategy_to_icans(strategy, icans)
else:
# The default behavior is to always pick the max version
icans = reversed(icans)

pinned = is_pinned(specifier)

for ican in icans:
if not (all_yanked and pinned) and ican.link.is_yanked:
Expand Down