Fullscreen eingestellt.

This commit is contained in:
2024-07-15 11:01:34 +02:00
parent 0c4926f0d5
commit a7955bc775
811 changed files with 9453 additions and 60540 deletions
@@ -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