Fullscreen eingestellt.
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -10,9 +10,6 @@ for sub-dependencies
|
||||
a. "first found, wins" (where the order is breadth first)
|
||||
"""
|
||||
|
||||
# The following comment should be removed at some point in the future.
|
||||
# mypy: strict-optional=False
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
@@ -52,7 +49,7 @@ from pip._internal.utils.packaging import check_requires_python
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]]
|
||||
DiscoveredDependencies = DefaultDict[Optional[str], List[InstallRequirement]]
|
||||
|
||||
|
||||
def _check_dist_requires_python(
|
||||
@@ -104,9 +101,8 @@ def _check_dist_requires_python(
|
||||
return
|
||||
|
||||
raise UnsupportedPythonVersion(
|
||||
"Package {!r} requires a different Python: {} not in {!r}".format(
|
||||
dist.raw_name, version, requires_python
|
||||
)
|
||||
f"Package {dist.raw_name!r} requires a different Python: "
|
||||
f"{version} not in {requires_python!r}"
|
||||
)
|
||||
|
||||
|
||||
@@ -246,9 +242,9 @@ class Resolver(BaseResolver):
|
||||
return [install_req], None
|
||||
|
||||
try:
|
||||
existing_req: Optional[
|
||||
InstallRequirement
|
||||
] = requirement_set.get_requirement(install_req.name)
|
||||
existing_req: Optional[InstallRequirement] = (
|
||||
requirement_set.get_requirement(install_req.name)
|
||||
)
|
||||
except KeyError:
|
||||
existing_req = None
|
||||
|
||||
@@ -263,9 +259,8 @@ class Resolver(BaseResolver):
|
||||
)
|
||||
if has_conflicting_requirement:
|
||||
raise InstallationError(
|
||||
"Double requirement given: {} (already in {}, name={!r})".format(
|
||||
install_req, existing_req, install_req.name
|
||||
)
|
||||
f"Double requirement given: {install_req} "
|
||||
f"(already in {existing_req}, name={install_req.name!r})"
|
||||
)
|
||||
|
||||
# When no existing requirement exists, add the requirement as a
|
||||
@@ -323,6 +318,7 @@ class Resolver(BaseResolver):
|
||||
"""
|
||||
# Don't uninstall the conflict if doing a user install and the
|
||||
# conflict is not a user install.
|
||||
assert req.satisfied_by is not None
|
||||
if not self.use_user_site or req.satisfied_by.in_usersite:
|
||||
req.should_reinstall = True
|
||||
req.satisfied_by = None
|
||||
@@ -421,6 +417,8 @@ class Resolver(BaseResolver):
|
||||
|
||||
if self.wheel_cache is None or self.preparer.require_hashes:
|
||||
return
|
||||
|
||||
assert req.link is not None, "_find_requirement_link unexpectedly returned None"
|
||||
cache_entry = self.wheel_cache.get_cache_entry(
|
||||
link=req.link,
|
||||
package_name=req.name,
|
||||
@@ -534,6 +532,7 @@ class Resolver(BaseResolver):
|
||||
with indent_log():
|
||||
# We add req_to_install before its dependencies, so that we
|
||||
# can refer to it when adding dependencies.
|
||||
assert req_to_install.name is not None
|
||||
if not requirement_set.has_requirement(req_to_install.name):
|
||||
# 'unnamed' requirements will get added here
|
||||
# 'unnamed' requirements can only come from being directly
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,15 +1,15 @@
|
||||
from typing import FrozenSet, Iterable, Optional, Tuple, Union
|
||||
from dataclasses import dataclass
|
||||
from typing import FrozenSet, Iterable, Optional, Tuple
|
||||
|
||||
from pip._vendor.packaging.specifiers import SpecifierSet
|
||||
from pip._vendor.packaging.utils import NormalizedName
|
||||
from pip._vendor.packaging.version import LegacyVersion, Version
|
||||
from pip._vendor.packaging.version import Version
|
||||
|
||||
from pip._internal.models.link import Link, links_equivalent
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.utils.hashes import Hashes
|
||||
|
||||
CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]]
|
||||
CandidateVersion = Union[LegacyVersion, Version]
|
||||
|
||||
|
||||
def format_name(project: NormalizedName, extras: FrozenSet[NormalizedName]) -> str:
|
||||
@@ -19,13 +19,11 @@ def format_name(project: NormalizedName, extras: FrozenSet[NormalizedName]) -> s
|
||||
return f"{project}[{extras_expr}]"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Constraint:
|
||||
def __init__(
|
||||
self, specifier: SpecifierSet, hashes: Hashes, links: FrozenSet[Link]
|
||||
) -> None:
|
||||
self.specifier = specifier
|
||||
self.hashes = hashes
|
||||
self.links = links
|
||||
specifier: SpecifierSet
|
||||
hashes: Hashes
|
||||
links: FrozenSet[Link]
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> "Constraint":
|
||||
@@ -116,7 +114,7 @@ class Candidate:
|
||||
raise NotImplementedError("Override in subclass")
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
def version(self) -> Version:
|
||||
raise NotImplementedError("Override in subclass")
|
||||
|
||||
@property
|
||||
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast
|
||||
|
||||
from pip._vendor.packaging.requirements import InvalidRequirement
|
||||
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
||||
from pip._vendor.packaging.version import Version
|
||||
|
||||
@@ -9,6 +10,7 @@ from pip._internal.exceptions import (
|
||||
HashError,
|
||||
InstallationSubprocessError,
|
||||
MetadataInconsistent,
|
||||
MetadataInvalid,
|
||||
)
|
||||
from pip._internal.metadata import BaseDistribution
|
||||
from pip._internal.models.link import Link, links_equivalent
|
||||
@@ -21,7 +23,7 @@ from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.utils.direct_url_helpers import direct_url_from_link
|
||||
from pip._internal.utils.misc import normalize_version_info
|
||||
|
||||
from .base import Candidate, CandidateVersion, Requirement, format_name
|
||||
from .base import Candidate, Requirement, format_name
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .factory import Factory
|
||||
@@ -145,7 +147,7 @@ class _InstallRequirementBackedCandidate(Candidate):
|
||||
ireq: InstallRequirement,
|
||||
factory: "Factory",
|
||||
name: Optional[NormalizedName] = None,
|
||||
version: Optional[CandidateVersion] = None,
|
||||
version: Optional[Version] = None,
|
||||
) -> None:
|
||||
self._link = link
|
||||
self._source_link = source_link
|
||||
@@ -154,6 +156,7 @@ class _InstallRequirementBackedCandidate(Candidate):
|
||||
self._name = name
|
||||
self._version = version
|
||||
self.dist = self._prepare()
|
||||
self._hash: Optional[int] = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} {self.version}"
|
||||
@@ -162,7 +165,11 @@ class _InstallRequirementBackedCandidate(Candidate):
|
||||
return f"{self.__class__.__name__}({str(self._link)!r})"
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__, self._link))
|
||||
if self._hash is not None:
|
||||
return self._hash
|
||||
|
||||
self._hash = hash((self.__class__, self._link))
|
||||
return self._hash
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
@@ -185,16 +192,15 @@ class _InstallRequirementBackedCandidate(Candidate):
|
||||
return self.project_name
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
def version(self) -> Version:
|
||||
if self._version is None:
|
||||
self._version = self.dist.version
|
||||
return self._version
|
||||
|
||||
def format_for_error(self) -> str:
|
||||
return "{} {} (from {})".format(
|
||||
self.name,
|
||||
self.version,
|
||||
self._link.file_path if self._link.is_file else self._link,
|
||||
return (
|
||||
f"{self.name} {self.version} "
|
||||
f"(from {self._link.file_path if self._link.is_file else self._link})"
|
||||
)
|
||||
|
||||
def _prepare_distribution(self) -> BaseDistribution:
|
||||
@@ -216,6 +222,13 @@ class _InstallRequirementBackedCandidate(Candidate):
|
||||
str(self._version),
|
||||
str(dist.version),
|
||||
)
|
||||
# check dependencies are valid
|
||||
# TODO performance: this means we iterate the dependencies at least twice,
|
||||
# we may want to cache parsed Requires-Dist
|
||||
try:
|
||||
list(dist.iter_dependencies(list(dist.iter_provided_extras())))
|
||||
except InvalidRequirement as e:
|
||||
raise MetadataInvalid(self._ireq, str(e))
|
||||
|
||||
def _prepare(self) -> BaseDistribution:
|
||||
try:
|
||||
@@ -253,7 +266,7 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
|
||||
template: InstallRequirement,
|
||||
factory: "Factory",
|
||||
name: Optional[NormalizedName] = None,
|
||||
version: Optional[CandidateVersion] = None,
|
||||
version: Optional[Version] = None,
|
||||
) -> None:
|
||||
source_link = link
|
||||
cache_entry = factory.get_wheel_cache_entry(source_link, name)
|
||||
@@ -269,9 +282,9 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
|
||||
# Version may not be present for PEP 508 direct URLs
|
||||
if version is not None:
|
||||
wheel_version = Version(wheel.version)
|
||||
assert version == wheel_version, "{!r} != {!r} for wheel {}".format(
|
||||
version, wheel_version, name
|
||||
)
|
||||
assert (
|
||||
version == wheel_version
|
||||
), f"{version!r} != {wheel_version!r} for wheel {name}"
|
||||
|
||||
if cache_entry is not None:
|
||||
assert ireq.link.is_wheel
|
||||
@@ -310,7 +323,7 @@ class EditableCandidate(_InstallRequirementBackedCandidate):
|
||||
template: InstallRequirement,
|
||||
factory: "Factory",
|
||||
name: Optional[NormalizedName] = None,
|
||||
version: Optional[CandidateVersion] = None,
|
||||
version: Optional[Version] = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
link=link,
|
||||
@@ -353,13 +366,13 @@ class AlreadyInstalledCandidate(Candidate):
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}({self.dist!r})"
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.__class__, self.name, self.version))
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, AlreadyInstalledCandidate):
|
||||
return NotImplemented
|
||||
return self.name == other.name and self.version == other.version
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, self.__class__):
|
||||
return self.name == other.name and self.version == other.version
|
||||
return False
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, self.version))
|
||||
|
||||
@property
|
||||
def project_name(self) -> NormalizedName:
|
||||
@@ -370,7 +383,7 @@ class AlreadyInstalledCandidate(Candidate):
|
||||
return self.project_name
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
def version(self) -> Version:
|
||||
if self._version is None:
|
||||
self._version = self.dist.version
|
||||
return self._version
|
||||
@@ -434,14 +447,6 @@ class ExtrasCandidate(Candidate):
|
||||
"""
|
||||
self.base = base
|
||||
self.extras = frozenset(canonicalize_name(e) for e in extras)
|
||||
# If any extras are requested in their non-normalized forms, keep track
|
||||
# of their raw values. This is needed when we look up dependencies
|
||||
# since PEP 685 has not been implemented for marker-matching, and using
|
||||
# the non-normalized extra for lookup ensures the user can select a
|
||||
# non-normalized extra in a package with its non-normalized form.
|
||||
# TODO: Remove this attribute when packaging is upgraded to support the
|
||||
# marker comparison logic specified in PEP 685.
|
||||
self._unnormalized_extras = extras.difference(self.extras)
|
||||
self._comes_from = comes_from if comes_from is not None else self.base._ireq
|
||||
|
||||
def __str__(self) -> str:
|
||||
@@ -469,7 +474,7 @@ class ExtrasCandidate(Candidate):
|
||||
return format_name(self.base.project_name, self.extras)
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
def version(self) -> Version:
|
||||
return self.base.version
|
||||
|
||||
def format_for_error(self) -> str:
|
||||
@@ -489,50 +494,6 @@ class ExtrasCandidate(Candidate):
|
||||
def source_link(self) -> Optional[Link]:
|
||||
return self.base.source_link
|
||||
|
||||
def _warn_invalid_extras(
|
||||
self,
|
||||
requested: FrozenSet[str],
|
||||
valid: FrozenSet[str],
|
||||
) -> None:
|
||||
"""Emit warnings for invalid extras being requested.
|
||||
|
||||
This emits a warning for each requested extra that is not in the
|
||||
candidate's ``Provides-Extra`` list.
|
||||
"""
|
||||
invalid_extras_to_warn = frozenset(
|
||||
extra
|
||||
for extra in requested
|
||||
if extra not in valid
|
||||
# If an extra is requested in an unnormalized form, skip warning
|
||||
# about the normalized form being missing.
|
||||
and extra in self.extras
|
||||
)
|
||||
if not invalid_extras_to_warn:
|
||||
return
|
||||
for extra in sorted(invalid_extras_to_warn):
|
||||
logger.warning(
|
||||
"%s %s does not provide the extra '%s'",
|
||||
self.base.name,
|
||||
self.version,
|
||||
extra,
|
||||
)
|
||||
|
||||
def _calculate_valid_requested_extras(self) -> FrozenSet[str]:
|
||||
"""Get a list of valid extras requested by this candidate.
|
||||
|
||||
The user (or upstream dependant) may have specified extras that the
|
||||
candidate doesn't support. Any unsupported extras are dropped, and each
|
||||
cause a warning to be logged here.
|
||||
"""
|
||||
requested_extras = self.extras.union(self._unnormalized_extras)
|
||||
valid_extras = frozenset(
|
||||
extra
|
||||
for extra in requested_extras
|
||||
if self.base.dist.is_extra_provided(extra)
|
||||
)
|
||||
self._warn_invalid_extras(requested_extras, valid_extras)
|
||||
return valid_extras
|
||||
|
||||
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
|
||||
factory = self.base._factory
|
||||
|
||||
@@ -542,7 +503,18 @@ class ExtrasCandidate(Candidate):
|
||||
if not with_requires:
|
||||
return
|
||||
|
||||
valid_extras = self._calculate_valid_requested_extras()
|
||||
# The user may have specified extras that the candidate doesn't
|
||||
# support. We ignore any unsupported extras here.
|
||||
valid_extras = self.extras.intersection(self.base.dist.iter_provided_extras())
|
||||
invalid_extras = self.extras.difference(self.base.dist.iter_provided_extras())
|
||||
for extra in sorted(invalid_extras):
|
||||
logger.warning(
|
||||
"%s %s does not provide the extra '%s'",
|
||||
self.base.name,
|
||||
self.version,
|
||||
extra,
|
||||
)
|
||||
|
||||
for r in self.base.dist.iter_dependencies(valid_extras):
|
||||
yield from factory.make_requirements_from_spec(
|
||||
str(r),
|
||||
@@ -584,7 +556,7 @@ class RequiresPythonCandidate(Candidate):
|
||||
return REQUIRES_PYTHON_IDENTIFIER
|
||||
|
||||
@property
|
||||
def version(self) -> CandidateVersion:
|
||||
def version(self) -> Version:
|
||||
return self._version
|
||||
|
||||
def format_for_error(self) -> str:
|
||||
|
||||
@@ -3,6 +3,7 @@ import functools
|
||||
import logging
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Callable,
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Iterable,
|
||||
@@ -11,6 +12,7 @@ from typing import (
|
||||
Mapping,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Set,
|
||||
Tuple,
|
||||
@@ -21,6 +23,7 @@ from typing import (
|
||||
from pip._vendor.packaging.requirements import InvalidRequirement
|
||||
from pip._vendor.packaging.specifiers import SpecifierSet
|
||||
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
||||
from pip._vendor.packaging.version import Version
|
||||
from pip._vendor.resolvelib import ResolutionImpossible
|
||||
|
||||
from pip._internal.cache import CacheEntry, WheelCache
|
||||
@@ -28,6 +31,7 @@ from pip._internal.exceptions import (
|
||||
DistributionNotFound,
|
||||
InstallationError,
|
||||
MetadataInconsistent,
|
||||
MetadataInvalid,
|
||||
UnsupportedPythonVersion,
|
||||
UnsupportedWheel,
|
||||
)
|
||||
@@ -50,7 +54,7 @@ from pip._internal.utils.hashes import Hashes
|
||||
from pip._internal.utils.packaging import get_requirement
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
from .base import Candidate, CandidateVersion, Constraint, Requirement
|
||||
from .base import Candidate, Constraint, Requirement
|
||||
from .candidates import (
|
||||
AlreadyInstalledCandidate,
|
||||
BaseCandidate,
|
||||
@@ -70,7 +74,6 @@ from .requirements import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Protocol
|
||||
|
||||
class ConflictCause(Protocol):
|
||||
requirement: RequiresPythonRequirement
|
||||
@@ -177,7 +180,7 @@ class Factory:
|
||||
extras: FrozenSet[str],
|
||||
template: InstallRequirement,
|
||||
name: Optional[NormalizedName],
|
||||
version: Optional[CandidateVersion],
|
||||
version: Optional[Version],
|
||||
) -> Optional[Candidate]:
|
||||
base: Optional[BaseCandidate] = self._make_base_candidate_from_link(
|
||||
link, template, name, version
|
||||
@@ -191,7 +194,7 @@ class Factory:
|
||||
link: Link,
|
||||
template: InstallRequirement,
|
||||
name: Optional[NormalizedName],
|
||||
version: Optional[CandidateVersion],
|
||||
version: Optional[Version],
|
||||
) -> Optional[BaseCandidate]:
|
||||
# TODO: Check already installed candidate, and use it if the link and
|
||||
# editable flag match.
|
||||
@@ -211,7 +214,7 @@ class Factory:
|
||||
name=name,
|
||||
version=version,
|
||||
)
|
||||
except MetadataInconsistent as e:
|
||||
except (MetadataInconsistent, MetadataInvalid) as e:
|
||||
logger.info(
|
||||
"Discarding [blue underline]%s[/]: [yellow]%s[reset]",
|
||||
link,
|
||||
@@ -391,6 +394,7 @@ class Factory:
|
||||
incompatibilities: Mapping[str, Iterator[Candidate]],
|
||||
constraint: Constraint,
|
||||
prefers_installed: bool,
|
||||
is_satisfied_by: Callable[[Requirement, Candidate], bool],
|
||||
) -> Iterable[Candidate]:
|
||||
# Collect basic lookup information from the requirements.
|
||||
explicit_candidates: Set[Candidate] = set()
|
||||
@@ -456,7 +460,7 @@ class Factory:
|
||||
for c in explicit_candidates
|
||||
if id(c) not in incompat_ids
|
||||
and constraint.is_satisfied_by(c)
|
||||
and all(req.is_satisfied_by(c) for req in requirements[identifier])
|
||||
and all(is_satisfied_by(req, c) for req in requirements[identifier])
|
||||
)
|
||||
|
||||
def _make_requirements_from_install_req(
|
||||
@@ -668,8 +672,8 @@ class Factory:
|
||||
cands = self._finder.find_all_candidates(req.project_name)
|
||||
skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
|
||||
|
||||
versions_set: Set[CandidateVersion] = set()
|
||||
yanked_versions_set: Set[CandidateVersion] = set()
|
||||
versions_set: Set[Version] = set()
|
||||
yanked_versions_set: Set[Version] = set()
|
||||
for c in cands:
|
||||
is_yanked = c.link.is_yanked if c.link else False
|
||||
if is_yanked:
|
||||
@@ -799,7 +803,7 @@ class Factory:
|
||||
+ "\n\n"
|
||||
+ "To fix this you could try to:\n"
|
||||
+ "1. loosen the range of package versions you've specified\n"
|
||||
+ "2. remove package versions to allow pip attempt to solve "
|
||||
+ "2. remove package versions to allow pip to attempt to solve "
|
||||
+ "the dependency conflict\n"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,13 +9,18 @@ something.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import logging
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Set, Tuple
|
||||
|
||||
from pip._vendor.packaging.version import _BaseVersion
|
||||
|
||||
from pip._internal.exceptions import MetadataInvalid
|
||||
|
||||
from .base import Candidate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
IndexCandidateInfo = Tuple[_BaseVersion, Callable[[], Optional[Candidate]]]
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -44,11 +49,25 @@ def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]:
|
||||
for version, func in infos:
|
||||
if version in versions_found:
|
||||
continue
|
||||
candidate = func()
|
||||
if candidate is None:
|
||||
continue
|
||||
yield candidate
|
||||
versions_found.add(version)
|
||||
try:
|
||||
candidate = func()
|
||||
except MetadataInvalid as e:
|
||||
logger.warning(
|
||||
"Ignoring version %s of %s since it has invalid metadata:\n"
|
||||
"%s\n"
|
||||
"Please use pip<24.1 if you need to use this version.",
|
||||
version,
|
||||
e.ireq.name,
|
||||
e,
|
||||
)
|
||||
# Mark version as found to avoid trying other candidates with the same
|
||||
# version, since they most likely have invalid metadata as well.
|
||||
versions_found.add(version)
|
||||
else:
|
||||
if candidate is None:
|
||||
continue
|
||||
yield candidate
|
||||
versions_found.add(version)
|
||||
|
||||
|
||||
def _iter_built_with_prepended(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import collections
|
||||
import math
|
||||
from functools import lru_cache
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Dict,
|
||||
@@ -234,8 +235,10 @@ class PipProvider(_ProviderBase):
|
||||
constraint=constraint,
|
||||
prefers_installed=(not _eligible_for_upgrade(identifier)),
|
||||
incompatibilities=incompatibilities,
|
||||
is_satisfied_by=self.is_satisfied_by,
|
||||
)
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool:
|
||||
return requirement.is_satisfied_by(candidate)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Any, Optional
|
||||
|
||||
from pip._vendor.packaging.specifiers import SpecifierSet
|
||||
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
||||
|
||||
@@ -17,6 +19,14 @@ class ExplicitRequirement(Requirement):
|
||||
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
|
||||
@@ -41,14 +51,36 @@ 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"
|
||||
@@ -96,14 +128,38 @@ class SpecifierWithoutExtrasRequirement(SpecifierRequirement):
|
||||
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:
|
||||
@@ -112,6 +168,21 @@ class RequiresPythonRequirement(Requirement):
|
||||
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
|
||||
@@ -148,6 +219,14 @@ class UnsatisfiableRequirement(Requirement):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user