Viewing file: requirements.py (7.88 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
from typing import Any, Optional
from pip._vendor.packaging.specifiers import SpecifierSet from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._internal.req.constructors import install_req_drop_extras from pip._internal.req.req_install import InstallRequirement
from .base import Candidate, CandidateLookup, Requirement, format_name
class ExplicitRequirement(Requirement): def __init__(self, candidate: Candidate) -> None: self.candidate = candidate
def __str__(self) -> str: return str(self.candidate)
def __repr__(self) -> str: return f"{self.__class__.__name__}({self.candidate!r})"
def __hash__(self) -> int: return hash(self.candidate)
def __eq__(self, other: Any) -> bool: if not isinstance(other, ExplicitRequirement): return False return self.candidate == other.candidate
@property def project_name(self) -> NormalizedName: # No need to canonicalize - the candidate did this return self.candidate.project_name
@property def name(self) -> str: # No need to canonicalize - the candidate did this return self.candidate.name
def format_for_error(self) -> str: return self.candidate.format_for_error()
def get_candidate_lookup(self) -> CandidateLookup: return self.candidate, None
def is_satisfied_by(self, candidate: Candidate) -> bool: return candidate == self.candidate
class SpecifierRequirement(Requirement): def __init__(self, ireq: InstallRequirement) -> None: assert ireq.link is None, "This is a link, not a specifier" self._ireq = ireq self._equal_cache: Optional[str] = None self._hash: Optional[int] = None self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
@property def _equal(self) -> str: if self._equal_cache is not None: return self._equal_cache
self._equal_cache = str(self._ireq) return self._equal_cache
def __str__(self) -> str: return str(self._ireq.req)
def __repr__(self) -> str: return f"{self.__class__.__name__}({str(self._ireq.req)!r})"
def __eq__(self, other: object) -> bool: if not isinstance(other, SpecifierRequirement): return NotImplemented return self._equal == other._equal
def __hash__(self) -> int: if self._hash is not None: return self._hash
self._hash = hash(self._equal) return self._hash
@property def project_name(self) -> NormalizedName: assert self._ireq.req, "Specifier-backed ireq is always PEP 508" return canonicalize_name(self._ireq.req.name)
@property def name(self) -> str: return format_name(self.project_name, self._extras)
def format_for_error(self) -> 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) -> CandidateLookup: return None, self._ireq
def is_satisfied_by(self, candidate: Candidate) -> bool: assert candidate.name == self.name, ( f"Internal issue: Candidate is not for this requirement " f"{candidate.name} vs {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. assert self._ireq.req, "Specifier-backed ireq is always PEP 508" spec = self._ireq.req.specifier return spec.contains(candidate.version, prereleases=True)
class SpecifierWithoutExtrasRequirement(SpecifierRequirement): """ Requirement backed by an install requirement on a base package. Trims extras from its install requirement if there are any. """
def __init__(self, ireq: InstallRequirement) -> None: assert ireq.link is None, "This is a link, not a specifier" self._ireq = install_req_drop_extras(ireq) self._equal_cache: Optional[str] = None self._hash: Optional[int] = None self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras)
@property def _equal(self) -> str: if self._equal_cache is not None: return self._equal_cache
self._equal_cache = str(self._ireq) return self._equal_cache
def __eq__(self, other: object) -> bool: if not isinstance(other, SpecifierWithoutExtrasRequirement): return NotImplemented return self._equal == other._equal
def __hash__(self) -> int: if self._hash is not None: return self._hash
self._hash = hash(self._equal) return self._hash
class RequiresPythonRequirement(Requirement): """A requirement representing Requires-Python metadata."""
def __init__(self, specifier: SpecifierSet, match: Candidate) -> None: self.specifier = specifier self._specifier_string = str(specifier) # for faster __eq__ self._hash: Optional[int] = None self._candidate = match
def __str__(self) -> str: return f"Python {self.specifier}"
def __repr__(self) -> str: return f"{self.__class__.__name__}({str(self.specifier)!r})"
def __hash__(self) -> int: if self._hash is not None: return self._hash
self._hash = hash((self._specifier_string, self._candidate)) return self._hash
def __eq__(self, other: Any) -> bool: if not isinstance(other, RequiresPythonRequirement): return False return ( self._specifier_string == other._specifier_string and self._candidate == other._candidate )
@property def project_name(self) -> NormalizedName: return self._candidate.project_name
@property def name(self) -> str: return self._candidate.name
def format_for_error(self) -> str: return str(self)
def get_candidate_lookup(self) -> CandidateLookup: if self.specifier.contains(self._candidate.version, prereleases=True): return self._candidate, None return None, None
def is_satisfied_by(self, candidate: 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)
class UnsatisfiableRequirement(Requirement): """A requirement that cannot be satisfied."""
def __init__(self, name: NormalizedName) -> None: self._name = name
def __str__(self) -> str: return f"{self._name} (unavailable)"
def __repr__(self) -> str: return f"{self.__class__.__name__}({str(self._name)!r})"
def __eq__(self, other: object) -> bool: if not isinstance(other, UnsatisfiableRequirement): return NotImplemented return self._name == other._name
def __hash__(self) -> int: return hash(self._name)
@property def project_name(self) -> NormalizedName: return self._name
@property def name(self) -> str: return self._name
def format_for_error(self) -> str: return str(self)
def get_candidate_lookup(self) -> CandidateLookup: return None, None
def is_satisfied_by(self, candidate: Candidate) -> bool: return False
|