123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- from pip._vendor.packaging.specifiers import SpecifierSet
- from pip._vendor.resolvelib.providers import AbstractProvider
- from pip._internal.utils.typing import MYPY_CHECK_RUNNING
- if MYPY_CHECK_RUNNING:
- from typing import (
- Any,
- Dict,
- Iterable,
- Optional,
- Sequence,
- Set,
- Tuple,
- Union,
- )
- from .base import Requirement, Candidate
- from .factory import Factory
- # Notes on the relationship between the provider, the factory, and the
- # candidate and requirement classes.
- #
- # The provider is a direct implementation of the resolvelib class. Its role
- # is to deliver the API that resolvelib expects.
- #
- # Rather than work with completely abstract "requirement" and "candidate"
- # concepts as resolvelib does, pip has concrete classes implementing these two
- # ideas. The API of Requirement and Candidate objects are defined in the base
- # classes, but essentially map fairly directly to the equivalent provider
- # methods. In particular, `find_matches` and `is_satisfied_by` are
- # requirement methods, and `get_dependencies` is a candidate method.
- #
- # The factory is the interface to pip's internal mechanisms. It is stateless,
- # and is created by the resolver and held as a property of the provider. It is
- # responsible for creating Requirement and Candidate objects, and provides
- # services to those objects (access to pip's finder and preparer).
- class PipProvider(AbstractProvider):
- def __init__(
- self,
- factory, # type: Factory
- constraints, # type: Dict[str, SpecifierSet]
- ignore_dependencies, # type: bool
- upgrade_strategy, # type: str
- user_requested, # type: Set[str]
- ):
- # type: (...) -> None
- self._factory = factory
- self._constraints = constraints
- self._ignore_dependencies = ignore_dependencies
- self._upgrade_strategy = upgrade_strategy
- self.user_requested = user_requested
- def _sort_matches(self, matches):
- # type: (Iterable[Candidate]) -> Sequence[Candidate]
- # The requirement is responsible for returning a sequence of potential
- # candidates, one per version. The provider handles the logic of
- # deciding the order in which these candidates should be passed to
- # the resolver.
- # The `matches` argument is a sequence of candidates, one per version,
- # which are potential options to be installed. The requirement will
- # have already sorted out whether to give us an already-installed
- # candidate or a version from PyPI (i.e., it will deal with options
- # like --force-reinstall and --ignore-installed).
- # We now work out the correct order.
- #
- # 1. If no other considerations apply, later versions take priority.
- # 2. An already installed distribution is preferred over any other,
- # unless the user has requested an upgrade.
- # Upgrades are allowed when:
- # * The --upgrade flag is set, and
- # - The project was specified on the command line, or
- # - The project is a dependency and the "eager" upgrade strategy
- # was requested.
- def _eligible_for_upgrade(name):
- # type: (str) -> bool
- """Are upgrades allowed for this project?
- This checks the upgrade strategy, and whether the project was one
- that the user specified in the command line, in order to decide
- whether we should upgrade if there's a newer version available.
- (Note that we don't need access to the `--upgrade` flag, because
- an upgrade strategy of "to-satisfy-only" means that `--upgrade`
- was not specified).
- """
- if self._upgrade_strategy == "eager":
- return True
- elif self._upgrade_strategy == "only-if-needed":
- return (name in self.user_requested)
- return False
- def sort_key(c):
- # type: (Candidate) -> int
- """Return a sort key for the matches.
- The highest priority should be given to installed candidates that
- are not eligible for upgrade. We use the integer value in the first
- part of the key to sort these before other candidates.
- We only pull the installed candidate to the bottom (i.e. most
- preferred), but otherwise keep the ordering returned by the
- requirement. The requirement is responsible for returning a list
- otherwise sorted for the resolver, taking account for versions
- and binary preferences as specified by the user.
- """
- if c.is_installed and not _eligible_for_upgrade(c.name):
- return 1
- return 0
- return sorted(matches, key=sort_key)
- def identify(self, dependency):
- # type: (Union[Requirement, Candidate]) -> str
- return dependency.name
- def get_preference(
- self,
- resolution, # type: Optional[Candidate]
- candidates, # type: Sequence[Candidate]
- information # type: Sequence[Tuple[Requirement, Candidate]]
- ):
- # type: (...) -> Any
- # Use the "usual" value for now
- return len(candidates)
- def find_matches(self, requirements):
- # type: (Sequence[Requirement]) -> Iterable[Candidate]
- if not requirements:
- return []
- constraint = self._constraints.get(
- requirements[0].name, SpecifierSet(),
- )
- candidates = self._factory.find_candidates(requirements, constraint)
- return reversed(self._sort_matches(candidates))
- def is_satisfied_by(self, requirement, candidate):
- # type: (Requirement, Candidate) -> bool
- return requirement.is_satisfied_by(candidate)
- def get_dependencies(self, candidate):
- # type: (Candidate) -> Sequence[Requirement]
- with_requires = not self._ignore_dependencies
- return [
- r
- for r in candidate.iter_dependencies(with_requires)
- if r is not None
- ]
|