138 lines
4.4 KiB
Python
138 lines
4.4 KiB
Python
|
from pip._vendor.packaging.utils import canonicalize_name
|
||
|
|
||
|
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||
|
|
||
|
from .base import Requirement, format_name
|
||
|
|
||
|
if MYPY_CHECK_RUNNING:
|
||
|
from pip._vendor.packaging.specifiers import SpecifierSet
|
||
|
|
||
|
from pip._internal.req.req_install import InstallRequirement
|
||
|
|
||
|
from .base import Candidate, CandidateLookup
|
||
|
|
||
|
|
||
|
class ExplicitRequirement(Requirement):
|
||
|
def __init__(self, candidate):
|
||
|
# type: (Candidate) -> None
|
||
|
self.candidate = candidate
|
||
|
|
||
|
def __repr__(self):
|
||
|
# type: () -> str
|
||
|
return "{class_name}({candidate!r})".format(
|
||
|
class_name=self.__class__.__name__,
|
||
|
candidate=self.candidate,
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
# type: () -> str
|
||
|
# No need to canonicalise - the candidate did this
|
||
|
return self.candidate.name
|
||
|
|
||
|
def format_for_error(self):
|
||
|
# type: () -> str
|
||
|
return self.candidate.format_for_error()
|
||
|
|
||
|
def get_candidate_lookup(self):
|
||
|
# type: () -> CandidateLookup
|
||
|
return self.candidate, None
|
||
|
|
||
|
def is_satisfied_by(self, candidate):
|
||
|
# type: (Candidate) -> bool
|
||
|
return candidate == self.candidate
|
||
|
|
||
|
|
||
|
class SpecifierRequirement(Requirement):
|
||
|
def __init__(self, ireq):
|
||
|
# type: (InstallRequirement) -> None
|
||
|
assert ireq.link is None, "This is a link, not a specifier"
|
||
|
self._ireq = ireq
|
||
|
self._extras = frozenset(ireq.extras)
|
||
|
|
||
|
def __str__(self):
|
||
|
# type: () -> str
|
||
|
return str(self._ireq.req)
|
||
|
|
||
|
def __repr__(self):
|
||
|
# type: () -> str
|
||
|
return "{class_name}({requirement!r})".format(
|
||
|
class_name=self.__class__.__name__,
|
||
|
requirement=str(self._ireq.req),
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
# type: () -> str
|
||
|
canonical_name = canonicalize_name(self._ireq.req.name)
|
||
|
return format_name(canonical_name, self._extras)
|
||
|
|
||
|
def format_for_error(self):
|
||
|
# type: () -> str
|
||
|
|
||
|
# Convert comma-separated specifiers into "A, B, ..., F and G"
|
||
|
# This makes the specifier a bit more "human readable", without
|
||
|
# risking a change in meaning. (Hopefully! Not all edge cases have
|
||
|
# been checked)
|
||
|
parts = [s.strip() for s in str(self).split(",")]
|
||
|
if len(parts) == 0:
|
||
|
return ""
|
||
|
elif len(parts) == 1:
|
||
|
return parts[0]
|
||
|
|
||
|
return ", ".join(parts[:-1]) + " and " + parts[-1]
|
||
|
|
||
|
def get_candidate_lookup(self):
|
||
|
# type: () -> CandidateLookup
|
||
|
return None, self._ireq
|
||
|
|
||
|
def is_satisfied_by(self, candidate):
|
||
|
# type: (Candidate) -> bool
|
||
|
assert candidate.name == self.name, \
|
||
|
"Internal issue: Candidate is not for this requirement " \
|
||
|
" {} vs {}".format(candidate.name, self.name)
|
||
|
# We can safely always allow prereleases here since PackageFinder
|
||
|
# already implements the prerelease logic, and would have filtered out
|
||
|
# prerelease candidates if the user does not expect them.
|
||
|
spec = self._ireq.req.specifier
|
||
|
return spec.contains(candidate.version, prereleases=True)
|
||
|
|
||
|
|
||
|
class RequiresPythonRequirement(Requirement):
|
||
|
"""A requirement representing Requires-Python metadata.
|
||
|
"""
|
||
|
def __init__(self, specifier, match):
|
||
|
# type: (SpecifierSet, Candidate) -> None
|
||
|
self.specifier = specifier
|
||
|
self._candidate = match
|
||
|
|
||
|
def __repr__(self):
|
||
|
# type: () -> str
|
||
|
return "{class_name}({specifier!r})".format(
|
||
|
class_name=self.__class__.__name__,
|
||
|
specifier=str(self.specifier),
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
# type: () -> str
|
||
|
return self._candidate.name
|
||
|
|
||
|
def format_for_error(self):
|
||
|
# type: () -> str
|
||
|
return "Python " + str(self.specifier)
|
||
|
|
||
|
def get_candidate_lookup(self):
|
||
|
# type: () -> CandidateLookup
|
||
|
if self.specifier.contains(self._candidate.version, prereleases=True):
|
||
|
return self._candidate, None
|
||
|
return None, None
|
||
|
|
||
|
def is_satisfied_by(self, candidate):
|
||
|
# type: (Candidate) -> bool
|
||
|
assert candidate.name == self._candidate.name, "Not Python candidate"
|
||
|
# We can safely always allow prereleases here since PackageFinder
|
||
|
# already implements the prerelease logic, and would have filtered out
|
||
|
# prerelease candidates if the user does not expect them.
|
||
|
return self.specifier.contains(candidate.version, prereleases=True)
|