[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -1,20 +1,24 @@
from typing import Callable, List, Optional
from __future__ import annotations
from typing import Callable
from typing import List
from typing import Optional
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
InstallRequirementProvider = Callable[
[str, Optional[InstallRequirement]], InstallRequirement
[str, Optional[InstallRequirement]], InstallRequirement,
]
class BaseResolver:
def resolve(
self, root_reqs: List[InstallRequirement], check_supported_wheels: bool
self, root_reqs: list[InstallRequirement], check_supported_wheels: bool,
) -> RequirementSet:
raise NotImplementedError()
def get_installation_order(
self, req_set: RequirementSet
) -> List[InstallRequirement]:
self, req_set: RequirementSet,
) -> list[InstallRequirement]:
raise NotImplementedError()

View file

@ -9,42 +9,43 @@ for top-level requirements:
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
from __future__ import annotations
import logging
import sys
from collections import defaultdict
from itertools import chain
from typing import DefaultDict, Iterable, List, Optional, Set, Tuple
from pip._vendor.packaging import specifiers
from pip._vendor.packaging.requirements import Requirement
from typing import DefaultDict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Set
from typing import Tuple
from pip._internal.cache import WheelCache
from pip._internal.exceptions import (
BestVersionAlreadyInstalled,
DistributionNotFound,
HashError,
HashErrors,
NoneMetadataError,
UnsupportedPythonVersion,
)
from pip._internal.exceptions import BestVersionAlreadyInstalled
from pip._internal.exceptions import DistributionNotFound
from pip._internal.exceptions import HashError
from pip._internal.exceptions import HashErrors
from pip._internal.exceptions import NoneMetadataError
from pip._internal.exceptions import UnsupportedPythonVersion
from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import BaseDistribution
from pip._internal.models.link import Link
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.req_install import (
InstallRequirement,
check_invalid_constraint_type,
)
from pip._internal.req.req_install import check_invalid_constraint_type
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
from pip._internal.resolution.base import BaseResolver
from pip._internal.resolution.base import InstallRequirementProvider
from pip._internal.utils.compatibility_tags import get_supported
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import normalize_version_info
from pip._internal.utils.packaging import check_requires_python
from pip._vendor.packaging import specifiers
from pip._vendor.packaging.requirements import Requirement
logger = logging.getLogger(__name__)
@ -53,7 +54,7 @@ DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]]
def _check_dist_requires_python(
dist: BaseDistribution,
version_info: Tuple[int, int, int],
version_info: tuple[int, int, int],
ignore_requires_python: bool = False,
) -> None:
"""
@ -82,17 +83,17 @@ def _check_dist_requires_python(
)
except specifiers.InvalidSpecifier as exc:
logger.warning(
"Package %r has an invalid Requires-Python: %s", dist.raw_name, exc
'Package %r has an invalid Requires-Python: %s', dist.raw_name, exc,
)
return
if is_compatible:
return
version = ".".join(map(str, version_info))
version = '.'.join(map(str, version_info))
if ignore_requires_python:
logger.debug(
"Ignoring failed Requires-Python check for package %r: %s not in %r",
'Ignoring failed Requires-Python check for package %r: %s not in %r',
dist.raw_name,
version,
requires_python,
@ -100,9 +101,9 @@ def _check_dist_requires_python(
return
raise UnsupportedPythonVersion(
"Package {!r} requires a different Python: {} not in {!r}".format(
dist.raw_name, version, requires_python
)
'Package {!r} requires a different Python: {} not in {!r}'.format(
dist.raw_name, version, requires_python,
),
)
@ -111,13 +112,13 @@ class Resolver(BaseResolver):
the requested operation without breaking the requirements of any package.
"""
_allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"}
_allowed_strategies = {'eager', 'only-if-needed', 'to-satisfy-only'}
def __init__(
self,
preparer: RequirementPreparer,
finder: PackageFinder,
wheel_cache: Optional[WheelCache],
wheel_cache: WheelCache | None,
make_install_req: InstallRequirementProvider,
use_user_site: bool,
ignore_dependencies: bool,
@ -125,7 +126,7 @@ class Resolver(BaseResolver):
ignore_requires_python: bool,
force_reinstall: bool,
upgrade_strategy: str,
py_version_info: Optional[Tuple[int, ...]] = None,
py_version_info: tuple[int, ...] | None = None,
) -> None:
super().__init__()
assert upgrade_strategy in self._allowed_strategies
@ -152,7 +153,7 @@ class Resolver(BaseResolver):
self._discovered_dependencies: DiscoveredDependencies = defaultdict(list)
def resolve(
self, root_reqs: List[InstallRequirement], check_supported_wheels: bool
self, root_reqs: list[InstallRequirement], check_supported_wheels: bool,
) -> RequirementSet:
"""Resolve what operations need to be done
@ -174,7 +175,7 @@ class Resolver(BaseResolver):
# exceptions cannot be checked ahead of time, because
# _populate_link() needs to be called before we can make decisions
# based on link type.
discovered_reqs: List[InstallRequirement] = []
discovered_reqs: list[InstallRequirement] = []
hash_errors = HashErrors()
for req in chain(requirement_set.all_requirements, discovered_reqs):
try:
@ -189,12 +190,12 @@ class Resolver(BaseResolver):
return requirement_set
def _is_upgrade_allowed(self, req: InstallRequirement) -> bool:
if self.upgrade_strategy == "to-satisfy-only":
if self.upgrade_strategy == 'to-satisfy-only':
return False
elif self.upgrade_strategy == "eager":
elif self.upgrade_strategy == 'eager':
return True
else:
assert self.upgrade_strategy == "only-if-needed"
assert self.upgrade_strategy == 'only-if-needed'
return req.user_supplied or req.constraint
def _set_req_to_reinstall(self, req: InstallRequirement) -> None:
@ -208,8 +209,8 @@ class Resolver(BaseResolver):
req.satisfied_by = None
def _check_skip_installed(
self, req_to_install: InstallRequirement
) -> Optional[str]:
self, req_to_install: InstallRequirement,
) -> str | None:
"""Check if req_to_install should be skipped.
This will check if the req is installed, and whether we should upgrade
@ -239,9 +240,9 @@ class Resolver(BaseResolver):
return None
if not self._is_upgrade_allowed(req_to_install):
if self.upgrade_strategy == "only-if-needed":
return "already satisfied, skipping upgrade"
return "already satisfied"
if self.upgrade_strategy == 'only-if-needed':
return 'already satisfied, skipping upgrade'
return 'already satisfied'
# Check for the possibility of an upgrade. For link-based
# requirements we have to pull the tree down and inspect to assess
@ -251,7 +252,7 @@ class Resolver(BaseResolver):
self.finder.find_requirement(req_to_install, upgrade=True)
except BestVersionAlreadyInstalled:
# Then the best version is installed.
return "already up-to-date"
return 'already up-to-date'
except DistributionNotFound:
# No distribution found, so we squash the error. It will
# be raised later when we re-try later to do the install.
@ -261,7 +262,7 @@ class Resolver(BaseResolver):
self._set_req_to_reinstall(req_to_install)
return None
def _find_requirement_link(self, req: InstallRequirement) -> Optional[Link]:
def _find_requirement_link(self, req: InstallRequirement) -> Link | None:
upgrade = self._is_upgrade_allowed(req)
best_candidate = self.finder.find_requirement(req, upgrade)
if not best_candidate:
@ -270,14 +271,14 @@ class Resolver(BaseResolver):
# Log a warning per PEP 592 if necessary before returning.
link = best_candidate.link
if link.is_yanked:
reason = link.yanked_reason or "<none given>"
reason = link.yanked_reason or '<none given>'
msg = (
# Mark this as a unicode string to prevent
# "UnicodeEncodeError: 'ascii' codec can't encode character"
# in Python 2 when the reason contains non-ascii characters.
"The candidate selected for download or install is a "
"yanked version: {candidate}\n"
"Reason for being yanked: {reason}"
'The candidate selected for download or install is a '
'yanked version: {candidate}\n'
'Reason for being yanked: {reason}'
).format(candidate=best_candidate, reason=reason)
logger.warning(msg)
@ -307,7 +308,7 @@ class Resolver(BaseResolver):
supported_tags=get_supported(),
)
if cache_entry is not None:
logger.debug("Using cached wheel link: %s", cache_entry.link)
logger.debug('Using cached wheel link: %s', cache_entry.link)
if req.link is req.original_link and cache_entry.persistent:
req.original_link_is_in_wheel_cache = True
req.link = cache_entry.link
@ -344,16 +345,16 @@ class Resolver(BaseResolver):
if req.satisfied_by:
should_modify = (
self.upgrade_strategy != "to-satisfy-only"
or self.force_reinstall
or self.ignore_installed
or req.link.scheme == "file"
self.upgrade_strategy != 'to-satisfy-only' or
self.force_reinstall or
self.ignore_installed or
req.link.scheme == 'file'
)
if should_modify:
self._set_req_to_reinstall(req)
else:
logger.info(
"Requirement already satisfied (use --upgrade to upgrade): %s",
'Requirement already satisfied (use --upgrade to upgrade): %s',
req,
)
return dist
@ -362,7 +363,7 @@ class Resolver(BaseResolver):
self,
requirement_set: RequirementSet,
req_to_install: InstallRequirement,
) -> List[InstallRequirement]:
) -> list[InstallRequirement]:
"""Prepare a single requirements file.
:return: A list of additional InstallRequirements to also install.
@ -385,7 +386,7 @@ class Resolver(BaseResolver):
ignore_requires_python=self.ignore_requires_python,
)
more_reqs: List[InstallRequirement] = []
more_reqs: list[InstallRequirement] = []
def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None:
# This idiosyncratically converts the Requirement to str and let
@ -415,11 +416,11 @@ class Resolver(BaseResolver):
if not self.ignore_dependencies:
if req_to_install.extras:
logger.debug(
"Installing extra requirements: %r",
",".join(req_to_install.extras),
'Installing extra requirements: %r',
','.join(req_to_install.extras),
)
missing_requested = sorted(
set(req_to_install.extras) - set(dist.iter_provided_extras())
set(req_to_install.extras) - set(dist.iter_provided_extras()),
)
for missing in missing_requested:
logger.warning(
@ -430,7 +431,7 @@ class Resolver(BaseResolver):
)
available_requested = sorted(
set(dist.iter_provided_extras()) & set(req_to_install.extras)
set(dist.iter_provided_extras()) & set(req_to_install.extras),
)
for subreq in dist.iter_dependencies(available_requested):
add_req(subreq, extras_requested=available_requested)
@ -438,8 +439,8 @@ class Resolver(BaseResolver):
return more_reqs
def get_installation_order(
self, req_set: RequirementSet
) -> List[InstallRequirement]:
self, req_set: RequirementSet,
) -> list[InstallRequirement]:
"""Create the installation order.
The installation order is topological - requirements are installed
@ -450,7 +451,7 @@ class Resolver(BaseResolver):
# installs the user specified things in the order given, except when
# dependencies must come earlier to achieve topological order.
order = []
ordered_reqs: Set[InstallRequirement] = set()
ordered_reqs: set[InstallRequirement] = set()
def schedule(req: InstallRequirement) -> None:
if req.satisfied_by or req in ordered_reqs:

View file

@ -1,45 +1,53 @@
from typing import FrozenSet, Iterable, Optional, Tuple, Union
from __future__ import annotations
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import LegacyVersion, Version
from typing import FrozenSet
from typing import Iterable
from typing import Optional
from typing import Tuple
from typing import Union
from pip._internal.models.link import Link, links_equivalent
from pip._internal.models.link import Link
from pip._internal.models.link import links_equivalent
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.hashes import Hashes
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.utils import NormalizedName
from pip._vendor.packaging.version import LegacyVersion
from pip._vendor.packaging.version import Version
CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]]
CandidateLookup = Tuple[Optional['Candidate'], Optional[InstallRequirement]]
CandidateVersion = Union[LegacyVersion, Version]
def format_name(project: str, extras: FrozenSet[str]) -> str:
def format_name(project: str, extras: frozenset[str]) -> str:
if not extras:
return project
canonical_extras = sorted(canonicalize_name(e) for e in extras)
return "{}[{}]".format(project, ",".join(canonical_extras))
return '{}[{}]'.format(project, ','.join(canonical_extras))
class Constraint:
def __init__(
self, specifier: SpecifierSet, hashes: Hashes, links: FrozenSet[Link]
self, specifier: SpecifierSet, hashes: Hashes, links: frozenset[Link],
) -> None:
self.specifier = specifier
self.hashes = hashes
self.links = links
@classmethod
def empty(cls) -> "Constraint":
def empty(cls) -> Constraint:
return Constraint(SpecifierSet(), Hashes(), frozenset())
@classmethod
def from_ireq(cls, ireq: InstallRequirement) -> "Constraint":
def from_ireq(cls, ireq: InstallRequirement) -> Constraint:
links = frozenset([ireq.link]) if ireq.link else frozenset()
return Constraint(ireq.specifier, ireq.hashes(trust_internet=False), links)
def __bool__(self) -> bool:
return bool(self.specifier) or bool(self.hashes) or bool(self.links)
def __and__(self, other: InstallRequirement) -> "Constraint":
def __and__(self, other: InstallRequirement) -> Constraint:
if not isinstance(other, InstallRequirement):
return NotImplemented
specifier = self.specifier & other.specifier
@ -49,7 +57,7 @@ class Constraint:
links = links.union([other.link])
return Constraint(specifier, hashes, links)
def is_satisfied_by(self, candidate: "Candidate") -> bool:
def is_satisfied_by(self, candidate: Candidate) -> bool:
# Reject if there are any mismatched URL constraints on this package.
if self.links and not all(_match_link(link, candidate) for link in self.links):
return False
@ -68,7 +76,7 @@ class Requirement:
in which case ``name`` would contain the ``[...]`` part, while this
refers to the name of the project.
"""
raise NotImplementedError("Subclass should override")
raise NotImplementedError('Subclass should override')
@property
def name(self) -> str:
@ -77,19 +85,19 @@ class Requirement:
This is different from ``project_name`` if this requirement contains
extras, where ``project_name`` would not contain the ``[...]`` part.
"""
raise NotImplementedError("Subclass should override")
raise NotImplementedError('Subclass should override')
def is_satisfied_by(self, candidate: "Candidate") -> bool:
def is_satisfied_by(self, candidate: Candidate) -> bool:
return False
def get_candidate_lookup(self) -> CandidateLookup:
raise NotImplementedError("Subclass should override")
raise NotImplementedError('Subclass should override')
def format_for_error(self) -> str:
raise NotImplementedError("Subclass should override")
raise NotImplementedError('Subclass should override')
def _match_link(link: Link, candidate: "Candidate") -> bool:
def _match_link(link: Link, candidate: Candidate) -> bool:
if candidate.source_link:
return links_equivalent(link, candidate.source_link)
return False
@ -104,7 +112,7 @@ class Candidate:
in which case ``name`` would contain the ``[...]`` part, while this
refers to the name of the project.
"""
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
@property
def name(self) -> str:
@ -113,29 +121,29 @@ class Candidate:
This is different from ``project_name`` if this candidate contains
extras, where ``project_name`` would not contain the ``[...]`` part.
"""
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
@property
def version(self) -> CandidateVersion:
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
@property
def is_installed(self) -> bool:
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
@property
def is_editable(self) -> bool:
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
@property
def source_link(self) -> Optional[Link]:
raise NotImplementedError("Override in subclass")
def source_link(self) -> Link | None:
raise NotImplementedError('Override in subclass')
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
raise NotImplementedError("Override in subclass")
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
raise NotImplementedError('Override in subclass')
def get_install_requirement(self) -> Optional[InstallRequirement]:
raise NotImplementedError("Override in subclass")
def get_install_requirement(self) -> InstallRequirement | None:
raise NotImplementedError('Override in subclass')
def format_for_error(self) -> str:
raise NotImplementedError("Subclass should override")
raise NotImplementedError('Subclass should override')

View file

@ -1,26 +1,35 @@
from __future__ import annotations
import logging
import sys
from typing import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast
from typing import Any
from typing import cast
from typing import FrozenSet
from typing import Iterable
from typing import Optional
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import Version
from pip._internal.exceptions import (
HashError,
InstallationSubprocessError,
MetadataInconsistent,
)
from pip._internal.exceptions import HashError
from pip._internal.exceptions import InstallationSubprocessError
from pip._internal.exceptions import MetadataInconsistent
from pip._internal.metadata import BaseDistribution
from pip._internal.models.link import Link, links_equivalent
from pip._internal.models.link import Link
from pip._internal.models.link import links_equivalent
from pip._internal.models.wheel import Wheel
from pip._internal.req.constructors import (
install_req_from_editable,
install_req_from_line,
)
from pip._internal.req.constructors import install_req_from_editable
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.misc import normalize_version_info
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.utils import NormalizedName
from pip._vendor.packaging.version import Version
from .base import Candidate, CandidateVersion, Requirement, format_name
from .base import Candidate
from .base import CandidateVersion
from .base import format_name
from .base import Requirement
if TYPE_CHECKING:
from .factory import Factory
@ -28,16 +37,16 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
BaseCandidate = Union[
"AlreadyInstalledCandidate",
"EditableCandidate",
"LinkCandidate",
'AlreadyInstalledCandidate',
'EditableCandidate',
'LinkCandidate',
]
# Avoid conflicting with the PyPI package "Python".
REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, "<Python from Requires-Python>")
REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, '<Python from Requires-Python>')
def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]:
def as_base_candidate(candidate: Candidate) -> BaseCandidate | None:
"""The runtime version of BaseCandidate."""
base_candidate_classes = (
AlreadyInstalledCandidate,
@ -50,9 +59,9 @@ def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]:
def make_install_req_from_link(
link: Link, template: InstallRequirement
link: Link, template: InstallRequirement,
) -> InstallRequirement:
assert not template.editable, "template is editable"
assert not template.editable, 'template is editable'
if template.req:
line = str(template.req)
else:
@ -76,9 +85,9 @@ def make_install_req_from_link(
def make_install_req_from_editable(
link: Link, template: InstallRequirement
link: Link, template: InstallRequirement,
) -> InstallRequirement:
assert template.editable, "template not editable"
assert template.editable, 'template not editable'
return install_req_from_editable(
link.url,
user_supplied=template.user_supplied,
@ -96,14 +105,14 @@ def make_install_req_from_editable(
def _make_install_req_from_dist(
dist: BaseDistribution, template: InstallRequirement
dist: BaseDistribution, template: InstallRequirement,
) -> InstallRequirement:
if template.req:
line = str(template.req)
elif template.link:
line = f"{dist.canonical_name} @ {template.link.url}"
line = f'{dist.canonical_name} @ {template.link.url}'
else:
line = f"{dist.canonical_name}=={dist.version}"
line = f'{dist.canonical_name}=={dist.version}'
ireq = install_req_from_line(
line,
user_supplied=template.user_supplied,
@ -145,9 +154,9 @@ class _InstallRequirementBackedCandidate(Candidate):
link: Link,
source_link: Link,
ireq: InstallRequirement,
factory: "Factory",
name: Optional[NormalizedName] = None,
version: Optional[CandidateVersion] = None,
factory: Factory,
name: NormalizedName | None = None,
version: CandidateVersion | None = None,
) -> None:
self._link = link
self._source_link = source_link
@ -158,10 +167,10 @@ class _InstallRequirementBackedCandidate(Candidate):
self.dist = self._prepare()
def __str__(self) -> str:
return f"{self.name} {self.version}"
return f'{self.name} {self.version}'
def __repr__(self) -> str:
return "{class_name}({link!r})".format(
return '{class_name}({link!r})'.format(
class_name=self.__class__.__name__,
link=str(self._link),
)
@ -175,7 +184,7 @@ class _InstallRequirementBackedCandidate(Candidate):
return False
@property
def source_link(self) -> Optional[Link]:
def source_link(self) -> Link | None:
return self._source_link
@property
@ -196,28 +205,28 @@ class _InstallRequirementBackedCandidate(Candidate):
return self._version
def format_for_error(self) -> str:
return "{} {} (from {})".format(
return '{} {} (from {})'.format(
self.name,
self.version,
self._link.file_path if self._link.is_file else self._link,
)
def _prepare_distribution(self) -> BaseDistribution:
raise NotImplementedError("Override in subclass")
raise NotImplementedError('Override in subclass')
def _check_metadata_consistency(self, dist: BaseDistribution) -> None:
"""Check for consistency of project name and version of dist."""
if self._name is not None and self._name != dist.canonical_name:
raise MetadataInconsistent(
self._ireq,
"name",
'name',
self._name,
dist.canonical_name,
)
if self._version is not None and self._version != dist.version:
raise MetadataInconsistent(
self._ireq,
"version",
'version',
str(self._version),
str(dist.version),
)
@ -233,19 +242,19 @@ class _InstallRequirementBackedCandidate(Candidate):
raise
except InstallationSubprocessError as exc:
# The output has been presented already, so don't duplicate it.
exc.context = "See above for output."
exc.context = 'See above for output.'
raise
self._check_metadata_consistency(dist)
return dist
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
requires = self.dist.iter_dependencies() if with_requires else ()
for r in requires:
yield self._factory.make_requirement_from_spec(str(r), self._ireq)
yield self._factory.make_requires_python_requirement(self.dist.requires_python)
def get_install_requirement(self) -> Optional[InstallRequirement]:
def get_install_requirement(self) -> InstallRequirement | None:
return self._ireq
@ -256,32 +265,32 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
self,
link: Link,
template: InstallRequirement,
factory: "Factory",
name: Optional[NormalizedName] = None,
version: Optional[CandidateVersion] = None,
factory: Factory,
name: NormalizedName | None = None,
version: CandidateVersion | None = None,
) -> None:
source_link = link
cache_entry = factory.get_wheel_cache_entry(link, name)
if cache_entry is not None:
logger.debug("Using cached wheel link: %s", cache_entry.link)
logger.debug('Using cached wheel link: %s', cache_entry.link)
link = cache_entry.link
ireq = make_install_req_from_link(link, template)
assert ireq.link == link
if ireq.link.is_wheel and not ireq.link.is_file:
wheel = Wheel(ireq.link.filename)
wheel_name = canonicalize_name(wheel.name)
assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel"
assert name == wheel_name, f'{name!r} != {wheel_name!r} for wheel'
# 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, '{!r} != {!r} for wheel {}'.format(
version, wheel_version, name,
)
if (
cache_entry is not None
and cache_entry.persistent
and template.link is template.original_link
cache_entry is not None and
cache_entry.persistent and
template.link is template.original_link
):
ireq.original_link_is_in_wheel_cache = True
@ -306,9 +315,9 @@ class EditableCandidate(_InstallRequirementBackedCandidate):
self,
link: Link,
template: InstallRequirement,
factory: "Factory",
name: Optional[NormalizedName] = None,
version: Optional[CandidateVersion] = None,
factory: Factory,
name: NormalizedName | None = None,
version: CandidateVersion | None = None,
) -> None:
super().__init__(
link=link,
@ -331,7 +340,7 @@ class AlreadyInstalledCandidate(Candidate):
self,
dist: BaseDistribution,
template: InstallRequirement,
factory: "Factory",
factory: Factory,
) -> None:
self.dist = dist
self._ireq = _make_install_req_from_dist(dist, template)
@ -341,14 +350,14 @@ class AlreadyInstalledCandidate(Candidate):
# The returned dist would be exactly the same as self.dist because we
# set satisfied_by in _make_install_req_from_dist.
# TODO: Supply reason based on force_reinstall and upgrade_strategy.
skip_reason = "already satisfied"
skip_reason = 'already satisfied'
factory.preparer.prepare_installed_requirement(self._ireq, skip_reason)
def __str__(self) -> str:
return str(self.dist)
def __repr__(self) -> str:
return "{class_name}({distribution!r})".format(
return '{class_name}({distribution!r})'.format(
class_name=self.__class__.__name__,
distribution=self.dist,
)
@ -378,15 +387,15 @@ class AlreadyInstalledCandidate(Candidate):
return self.dist.editable
def format_for_error(self) -> str:
return f"{self.name} {self.version} (Installed)"
return f'{self.name} {self.version} (Installed)'
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
if not with_requires:
return
for r in self.dist.iter_dependencies():
yield self._factory.make_requirement_from_spec(str(r), self._ireq)
def get_install_requirement(self) -> Optional[InstallRequirement]:
def get_install_requirement(self) -> InstallRequirement | None:
return None
@ -418,17 +427,17 @@ class ExtrasCandidate(Candidate):
def __init__(
self,
base: BaseCandidate,
extras: FrozenSet[str],
extras: frozenset[str],
) -> None:
self.base = base
self.extras = extras
def __str__(self) -> str:
name, rest = str(self.base).split(" ", 1)
return "{}[{}] {}".format(name, ",".join(self.extras), rest)
name, rest = str(self.base).split(' ', 1)
return '{}[{}] {}'.format(name, ','.join(self.extras), rest)
def __repr__(self) -> str:
return "{class_name}(base={base!r}, extras={extras!r})".format(
return '{class_name}(base={base!r}, extras={extras!r})'.format(
class_name=self.__class__.__name__,
base=self.base,
extras=self.extras,
@ -456,8 +465,8 @@ class ExtrasCandidate(Candidate):
return self.base.version
def format_for_error(self) -> str:
return "{} [{}]".format(
self.base.format_for_error(), ", ".join(sorted(self.extras))
return '{} [{}]'.format(
self.base.format_for_error(), ', '.join(sorted(self.extras)),
)
@property
@ -469,10 +478,10 @@ class ExtrasCandidate(Candidate):
return self.base.is_editable
@property
def source_link(self) -> Optional[Link]:
def source_link(self) -> Link | None:
return self.base.source_link
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
factory = self.base._factory
# Add a dependency on the exact base
@ -495,12 +504,12 @@ class ExtrasCandidate(Candidate):
for r in self.base.dist.iter_dependencies(valid_extras):
requirement = factory.make_requirement_from_spec(
str(r), self.base._ireq, valid_extras
str(r), self.base._ireq, valid_extras,
)
if requirement:
yield requirement
def get_install_requirement(self) -> Optional[InstallRequirement]:
def get_install_requirement(self) -> InstallRequirement | None:
# We don't return anything here, because we always
# depend on the base candidate, and we'll get the
# install requirement from that.
@ -511,19 +520,19 @@ class RequiresPythonCandidate(Candidate):
is_installed = False
source_link = None
def __init__(self, py_version_info: Optional[Tuple[int, ...]]) -> None:
def __init__(self, py_version_info: tuple[int, ...] | None) -> None:
if py_version_info is not None:
version_info = normalize_version_info(py_version_info)
else:
version_info = sys.version_info[:3]
self._version = Version(".".join(str(c) for c in version_info))
self._version = Version('.'.join(str(c) for c in version_info))
# We don't need to implement __eq__() and __ne__() since there is always
# only one RequiresPythonCandidate in a resolution, i.e. the host Python.
# The built-in object.__eq__() and object.__ne__() do exactly what we want.
def __str__(self) -> str:
return f"Python {self._version}"
return f'Python {self._version}'
@property
def project_name(self) -> NormalizedName:
@ -538,10 +547,10 @@ class RequiresPythonCandidate(Candidate):
return self._version
def format_for_error(self) -> str:
return f"Python {self.version}"
return f'Python {self.version}'
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]:
return ()
def get_install_requirement(self) -> Optional[InstallRequirement]:
def get_install_requirement(self) -> InstallRequirement | None:
return None

View file

@ -1,70 +1,68 @@
from __future__ import annotations
import contextlib
import functools
import logging
from typing import (
TYPE_CHECKING,
Dict,
FrozenSet,
Iterable,
Iterator,
List,
Mapping,
NamedTuple,
Optional,
Sequence,
Set,
Tuple,
TypeVar,
cast,
)
from typing import cast
from typing import Dict
from typing import FrozenSet
from typing import Iterable
from typing import Iterator
from typing import List
from typing import Mapping
from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypeVar
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.resolvelib import ResolutionImpossible
from pip._internal.cache import CacheEntry, WheelCache
from pip._internal.exceptions import (
DistributionNotFound,
InstallationError,
InstallationSubprocessError,
MetadataInconsistent,
UnsupportedPythonVersion,
UnsupportedWheel,
)
from pip._internal.cache import CacheEntry
from pip._internal.cache import WheelCache
from pip._internal.exceptions import DistributionNotFound
from pip._internal.exceptions import InstallationError
from pip._internal.exceptions import InstallationSubprocessError
from pip._internal.exceptions import MetadataInconsistent
from pip._internal.exceptions import UnsupportedPythonVersion
from pip._internal.exceptions import UnsupportedWheel
from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import BaseDistribution, get_default_environment
from pip._internal.metadata import BaseDistribution
from pip._internal.metadata import get_default_environment
from pip._internal.models.link import Link
from pip._internal.models.wheel import Wheel
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.constructors import install_req_from_link_and_ireq
from pip._internal.req.req_install import (
InstallRequirement,
check_invalid_constraint_type,
)
from pip._internal.req.req_install import check_invalid_constraint_type
from pip._internal.req.req_install import InstallRequirement
from pip._internal.resolution.base import InstallRequirementProvider
from pip._internal.utils.compatibility_tags import get_supported
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 pip._vendor.packaging.requirements import InvalidRequirement
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.utils import NormalizedName
from pip._vendor.resolvelib import ResolutionImpossible
from .base import Candidate, CandidateVersion, Constraint, Requirement
from .candidates import (
AlreadyInstalledCandidate,
BaseCandidate,
EditableCandidate,
ExtrasCandidate,
LinkCandidate,
RequiresPythonCandidate,
as_base_candidate,
)
from .found_candidates import FoundCandidates, IndexCandidateInfo
from .requirements import (
ExplicitRequirement,
RequiresPythonRequirement,
SpecifierRequirement,
UnsatisfiableRequirement,
)
from .base import Candidate
from .base import CandidateVersion
from .base import Constraint
from .base import Requirement
from .candidates import AlreadyInstalledCandidate
from .candidates import as_base_candidate
from .candidates import BaseCandidate
from .candidates import EditableCandidate
from .candidates import ExtrasCandidate
from .candidates import LinkCandidate
from .candidates import RequiresPythonCandidate
from .found_candidates import FoundCandidates
from .found_candidates import IndexCandidateInfo
from .requirements import ExplicitRequirement
from .requirements import RequiresPythonRequirement
from .requirements import SpecifierRequirement
from .requirements import UnsatisfiableRequirement
if TYPE_CHECKING:
from typing import Protocol
@ -76,14 +74,14 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
C = TypeVar("C")
C = TypeVar('C')
Cache = Dict[Link, C]
class CollectedRootRequirements(NamedTuple):
requirements: List[Requirement]
constraints: Dict[str, Constraint]
user_requested: Dict[str, int]
requirements: list[Requirement]
constraints: dict[str, Constraint]
user_requested: dict[str, int]
class Factory:
@ -92,13 +90,13 @@ class Factory:
finder: PackageFinder,
preparer: RequirementPreparer,
make_install_req: InstallRequirementProvider,
wheel_cache: Optional[WheelCache],
wheel_cache: WheelCache | None,
use_user_site: bool,
force_reinstall: bool,
ignore_installed: bool,
ignore_requires_python: bool,
suppress_build_failures: bool,
py_version_info: Optional[Tuple[int, ...]] = None,
py_version_info: tuple[int, ...] | None = None,
) -> None:
self._finder = finder
self.preparer = preparer
@ -113,9 +111,9 @@ class Factory:
self._build_failures: Cache[InstallationError] = {}
self._link_candidate_cache: Cache[LinkCandidate] = {}
self._editable_candidate_cache: Cache[EditableCandidate] = {}
self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {}
self._extras_candidate_cache: Dict[
Tuple[int, FrozenSet[str]], ExtrasCandidate
self._installed_candidate_cache: dict[str, AlreadyInstalledCandidate] = {}
self._extras_candidate_cache: dict[
tuple[int, frozenset[str]], ExtrasCandidate,
] = {}
if not ignore_installed:
@ -137,11 +135,11 @@ class Factory:
wheel = Wheel(link.filename)
if wheel.supported(self._finder.target_python.get_tags()):
return
msg = f"{link.filename} is not a supported wheel on this platform."
msg = f'{link.filename} is not a supported wheel on this platform.'
raise UnsupportedWheel(msg)
def _make_extras_candidate(
self, base: BaseCandidate, extras: FrozenSet[str]
self, base: BaseCandidate, extras: frozenset[str],
) -> ExtrasCandidate:
cache_key = (id(base), extras)
try:
@ -154,7 +152,7 @@ class Factory:
def _make_candidate_from_dist(
self,
dist: BaseDistribution,
extras: FrozenSet[str],
extras: frozenset[str],
template: InstallRequirement,
) -> Candidate:
try:
@ -169,11 +167,11 @@ class Factory:
def _make_candidate_from_link(
self,
link: Link,
extras: FrozenSet[str],
extras: frozenset[str],
template: InstallRequirement,
name: Optional[NormalizedName],
version: Optional[CandidateVersion],
) -> Optional[Candidate]:
name: NormalizedName | None,
version: CandidateVersion | None,
) -> Candidate | None:
# TODO: Check already installed candidate, and use it if the link and
# editable flag match.
@ -194,17 +192,17 @@ class Factory:
)
except MetadataInconsistent as e:
logger.info(
"Discarding [blue underline]%s[/]: [yellow]%s[reset]",
'Discarding [blue underline]%s[/]: [yellow]%s[reset]',
link,
e,
extra={"markup": True},
extra={'markup': True},
)
self._build_failures[link] = e
return None
except InstallationSubprocessError as e:
if not self._suppress_build_failures:
raise
logger.warning("Discarding %s due to build failure: %s", link, e)
logger.warning('Discarding %s due to build failure: %s', link, e)
self._build_failures[link] = e
return None
@ -221,17 +219,17 @@ class Factory:
)
except MetadataInconsistent as e:
logger.info(
"Discarding [blue underline]%s[/]: [yellow]%s[reset]",
'Discarding [blue underline]%s[/]: [yellow]%s[reset]',
link,
e,
extra={"markup": True},
extra={'markup': True},
)
self._build_failures[link] = e
return None
except InstallationSubprocessError as e:
if not self._suppress_build_failures:
raise
logger.warning("Discarding %s due to build failure: %s", link, e)
logger.warning('Discarding %s due to build failure: %s', link, e)
self._build_failures[link] = e
return None
base = self._link_candidate_cache[link]
@ -246,7 +244,7 @@ class Factory:
specifier: SpecifierSet,
hashes: Hashes,
prefers_installed: bool,
incompatible_ids: Set[int],
incompatible_ids: set[int],
) -> Iterable[Candidate]:
if not ireqs:
return ()
@ -256,17 +254,17 @@ class Factory:
# all of them.
# Hopefully the Project model can correct this mismatch in the future.
template = ireqs[0]
assert template.req, "Candidates found on index must be PEP 508"
assert template.req, 'Candidates found on index must be PEP 508'
name = canonicalize_name(template.req.name)
extras: FrozenSet[str] = frozenset()
extras: frozenset[str] = frozenset()
for ireq in ireqs:
assert ireq.req, "Candidates found on index must be PEP 508"
assert ireq.req, 'Candidates found on index must be PEP 508'
specifier &= ireq.req.specifier
hashes &= ireq.hashes(trust_internet=False)
extras |= frozenset(ireq.extras)
def _get_installed_candidate() -> Optional[Candidate]:
def _get_installed_candidate() -> Candidate | None:
"""Get the candidate for the currently-installed version."""
# If --force-reinstall is set, we want the version from the index
# instead, so we "pretend" there is nothing installed.
@ -305,11 +303,11 @@ class Factory:
def is_pinned(specifier: SpecifierSet) -> bool:
for sp in specifier:
if sp.operator == "===":
if sp.operator == '===':
return True
if sp.operator != "==":
if sp.operator != '==':
continue
if sp.version.endswith(".*"):
if sp.version.endswith('.*'):
continue
return True
return False
@ -340,7 +338,7 @@ class Factory:
def _iter_explicit_candidates_from_base(
self,
base_requirements: Iterable[Requirement],
extras: FrozenSet[str],
extras: frozenset[str],
) -> Iterator[Candidate]:
"""Produce explicit candidates from the base given an extra-ed package.
@ -356,7 +354,7 @@ class Factory:
# We've stripped extras from the identifier, and should always
# get a BaseCandidate here, unless there's a bug elsewhere.
base_cand = as_base_candidate(lookup_cand)
assert base_cand is not None, "no extras here"
assert base_cand is not None, 'no extras here'
yield self._make_extras_candidate(base_cand, extras)
def _iter_candidates_from_constraints(
@ -391,8 +389,8 @@ class Factory:
prefers_installed: bool,
) -> Iterable[Candidate]:
# Collect basic lookup information from the requirements.
explicit_candidates: Set[Candidate] = set()
ireqs: List[InstallRequirement] = []
explicit_candidates: set[Candidate] = set()
ireqs: list[InstallRequirement] = []
for req in requirements[identifier]:
cand, ireq = req.get_candidate_lookup()
if cand is not None:
@ -447,14 +445,14 @@ class Factory:
return (
c
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])
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])
)
def _make_requirement_from_install_req(
self, ireq: InstallRequirement, requested_extras: Iterable[str]
) -> Optional[Requirement]:
self, ireq: InstallRequirement, requested_extras: Iterable[str],
) -> Requirement | None:
if not ireq.match_markers(requested_extras):
logger.info(
"Ignoring %s: markers '%s' don't match your environment",
@ -485,7 +483,7 @@ class Factory:
return self.make_requirement_from_candidate(cand)
def collect_root_requirements(
self, root_ireqs: List[InstallRequirement]
self, root_ireqs: list[InstallRequirement],
) -> CollectedRootRequirements:
collected = CollectedRootRequirements([], {}, {})
for i, ireq in enumerate(root_ireqs):
@ -496,7 +494,7 @@ class Factory:
raise InstallationError(problem)
if not ireq.match_markers():
continue
assert ireq.name, "Constraint must be named"
assert ireq.name, 'Constraint must be named'
name = canonicalize_name(ireq.name)
if name in collected.constraints:
collected.constraints[name] &= ireq
@ -515,23 +513,23 @@ class Factory:
return collected
def make_requirement_from_candidate(
self, candidate: Candidate
self, candidate: Candidate,
) -> ExplicitRequirement:
return ExplicitRequirement(candidate)
def make_requirement_from_spec(
self,
specifier: str,
comes_from: Optional[InstallRequirement],
comes_from: InstallRequirement | None,
requested_extras: Iterable[str] = (),
) -> Optional[Requirement]:
) -> Requirement | None:
ireq = self._make_install_req_from_spec(specifier, comes_from)
return self._make_requirement_from_install_req(ireq, requested_extras)
def make_requires_python_requirement(
self,
specifier: SpecifierSet,
) -> Optional[Requirement]:
) -> Requirement | None:
if self._ignore_requires_python:
return None
# Don't bother creating a dependency for an empty Requires-Python.
@ -540,8 +538,8 @@ class Factory:
return RequiresPythonRequirement(specifier, self._python_candidate)
def get_wheel_cache_entry(
self, link: Link, name: Optional[str]
) -> Optional[CacheEntry]:
self, link: Link, name: str | None,
) -> CacheEntry | None:
"""Look up the link in the wheel cache.
If ``preparer.require_hashes`` is True, don't use the wheel cache,
@ -558,7 +556,7 @@ class Factory:
supported_tags=get_supported(),
)
def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]:
def get_dist_to_uninstall(self, candidate: Candidate) -> BaseDistribution | None:
# TODO: Are there more cases this needs to return True? Editable?
dist = self._installed_dists.get(candidate.project_name)
if dist is None: # Not installed, no uninstallation required.
@ -580,82 +578,82 @@ class Factory:
# in virtual environments, so we error out.
if running_under_virtualenv() and dist.in_site_packages:
message = (
f"Will not install to the user site because it will lack "
f"sys.path precedence to {dist.raw_name} in {dist.location}"
f'Will not install to the user site because it will lack '
f'sys.path precedence to {dist.raw_name} in {dist.location}'
)
raise InstallationError(message)
return None
def _report_requires_python_error(
self, causes: Sequence["ConflictCause"]
self, causes: Sequence[ConflictCause],
) -> UnsupportedPythonVersion:
assert causes, "Requires-Python error reported with no cause"
assert causes, 'Requires-Python error reported with no cause'
version = self._python_candidate.version
if len(causes) == 1:
specifier = str(causes[0].requirement.specifier)
message = (
f"Package {causes[0].parent.name!r} requires a different "
f"Python: {version} not in {specifier!r}"
f'Package {causes[0].parent.name!r} requires a different '
f'Python: {version} not in {specifier!r}'
)
return UnsupportedPythonVersion(message)
message = f"Packages require a different Python. {version} not in:"
message = f'Packages require a different Python. {version} not in:'
for cause in causes:
package = cause.parent.format_for_error()
specifier = str(cause.requirement.specifier)
message += f"\n{specifier!r} (required by {package})"
message += f'\n{specifier!r} (required by {package})'
return UnsupportedPythonVersion(message)
def _report_single_requirement_conflict(
self, req: Requirement, parent: Optional[Candidate]
self, req: Requirement, parent: Candidate | None,
) -> DistributionNotFound:
if parent is None:
req_disp = str(req)
else:
req_disp = f"{req} (from {parent.name})"
req_disp = f'{req} (from {parent.name})'
cands = self._finder.find_all_candidates(req.project_name)
versions = [str(v) for v in sorted({c.version for c in cands})]
logger.critical(
"Could not find a version that satisfies the requirement %s "
"(from versions: %s)",
'Could not find a version that satisfies the requirement %s '
'(from versions: %s)',
req_disp,
", ".join(versions) or "none",
', '.join(versions) or 'none',
)
if str(req) == "requirements.txt":
if str(req) == 'requirements.txt':
logger.info(
"HINT: You are attempting to install a package literally "
'HINT: You are attempting to install a package literally '
'named "requirements.txt" (which cannot exist). Consider '
"using the '-r' flag to install the packages listed in "
"requirements.txt"
'requirements.txt',
)
return DistributionNotFound(f"No matching distribution found for {req}")
return DistributionNotFound(f'No matching distribution found for {req}')
def get_installation_error(
self,
e: "ResolutionImpossible[Requirement, Candidate]",
constraints: Dict[str, Constraint],
e: ResolutionImpossible[Requirement, Candidate],
constraints: dict[str, Constraint],
) -> InstallationError:
assert e.causes, "Installation error reported with no cause"
assert e.causes, 'Installation error reported with no cause'
# If one of the things we can't solve is "we need Python X.Y",
# that is what we report.
requires_python_causes = [
cause
for cause in e.causes
if isinstance(cause.requirement, RequiresPythonRequirement)
and not cause.requirement.is_satisfied_by(self._python_candidate)
if isinstance(cause.requirement, RequiresPythonRequirement) and
not cause.requirement.is_satisfied_by(self._python_candidate)
]
if requires_python_causes:
# The comprehension above makes sure all Requirement instances are
# RequiresPythonRequirement, so let's cast for convenience.
return self._report_requires_python_error(
cast("Sequence[ConflictCause]", requires_python_causes),
cast('Sequence[ConflictCause]', requires_python_causes),
)
# Otherwise, we have a set of causes which can't all be satisfied
@ -672,16 +670,16 @@ class Factory:
# satisfied at once.
# A couple of formatting helpers
def text_join(parts: List[str]) -> str:
def text_join(parts: list[str]) -> str:
if len(parts) == 1:
return parts[0]
return ", ".join(parts[:-1]) + " and " + parts[-1]
return ', '.join(parts[:-1]) + ' and ' + parts[-1]
def describe_trigger(parent: Candidate) -> str:
ireq = parent.get_install_requirement()
if not ireq or not ireq.comes_from:
return f"{parent.name}=={parent.version}"
return f'{parent.name}=={parent.version}'
if isinstance(ireq.comes_from, InstallRequirement):
return str(ireq.comes_from.name)
return str(ireq.comes_from)
@ -698,42 +696,42 @@ class Factory:
if triggers:
info = text_join(sorted(triggers))
else:
info = "the requested packages"
info = 'the requested packages'
msg = (
"Cannot install {} because these package versions "
"have conflicting dependencies.".format(info)
'Cannot install {} because these package versions '
'have conflicting dependencies.'.format(info)
)
logger.critical(msg)
msg = "\nThe conflict is caused by:"
msg = '\nThe conflict is caused by:'
relevant_constraints = set()
for req, parent in e.causes:
if req.name in constraints:
relevant_constraints.add(req.name)
msg = msg + "\n "
msg = msg + '\n '
if parent:
msg = msg + f"{parent.name} {parent.version} depends on "
msg = msg + f'{parent.name} {parent.version} depends on '
else:
msg = msg + "The user requested "
msg = msg + 'The user requested '
msg = msg + req.format_for_error()
for key in relevant_constraints:
spec = constraints[key].specifier
msg += f"\n The user requested (constraint) {key}{spec}"
msg += f'\n The user requested (constraint) {key}{spec}'
msg = (
msg
+ "\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 "
+ "the dependency conflict\n"
msg +
'\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 ' +
'the dependency conflict\n'
)
logger.info(msg)
return DistributionNotFound(
"ResolutionImpossible: for help visit "
"https://pip.pypa.io/en/latest/topics/dependency-resolution/"
"#dealing-with-dependency-conflicts"
'ResolutionImpossible: for help visit '
'https://pip.pypa.io/en/latest/topics/dependency-resolution/'
'#dealing-with-dependency-conflicts',
)

View file

@ -7,10 +7,17 @@ everything here lazy all the way down, so we only touch candidates that we
absolutely need, and not "download the world" when we only need one version of
something.
"""
from __future__ import annotations
import functools
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Set, Tuple
from typing import Any
from typing import Callable
from typing import Iterator
from typing import Optional
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
from pip._vendor.packaging.version import _BaseVersion
@ -40,7 +47,7 @@ def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]:
This iterator is used when the package is not already installed. Candidates
from index come later in their normal ordering.
"""
versions_found: Set[_BaseVersion] = set()
versions_found: set[_BaseVersion] = set()
for version, func in infos:
if version in versions_found:
continue
@ -52,7 +59,7 @@ def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]:
def _iter_built_with_prepended(
installed: Candidate, infos: Iterator[IndexCandidateInfo]
installed: Candidate, infos: Iterator[IndexCandidateInfo],
) -> Iterator[Candidate]:
"""Iterator for ``FoundCandidates``.
@ -62,7 +69,7 @@ def _iter_built_with_prepended(
normal ordering, except skipped when the version is already installed.
"""
yield installed
versions_found: Set[_BaseVersion] = {installed.version}
versions_found: set[_BaseVersion] = {installed.version}
for version, func in infos:
if version in versions_found:
continue
@ -74,7 +81,7 @@ def _iter_built_with_prepended(
def _iter_built_with_inserted(
installed: Candidate, infos: Iterator[IndexCandidateInfo]
installed: Candidate, infos: Iterator[IndexCandidateInfo],
) -> Iterator[Candidate]:
"""Iterator for ``FoundCandidates``.
@ -86,7 +93,7 @@ def _iter_built_with_inserted(
the installed candidate exactly once before we start yielding older or
equivalent candidates, or after all other candidates if they are all newer.
"""
versions_found: Set[_BaseVersion] = set()
versions_found: set[_BaseVersion] = set()
for version, func in infos:
if version in versions_found:
continue
@ -117,9 +124,9 @@ class FoundCandidates(SequenceCandidate):
def __init__(
self,
get_infos: Callable[[], Iterator[IndexCandidateInfo]],
installed: Optional[Candidate],
installed: Candidate | None,
prefers_installed: bool,
incompatible_ids: Set[int],
incompatible_ids: set[int],
):
self._get_infos = get_infos
self._installed = installed

View file

@ -1,19 +1,21 @@
from __future__ import annotations
import collections
import math
from typing import (
TYPE_CHECKING,
Dict,
Iterable,
Iterator,
Mapping,
Sequence,
TypeVar,
Union,
)
from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import Mapping
from typing import Sequence
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
from pip._vendor.resolvelib.providers import AbstractProvider
from .base import Candidate, Constraint, Requirement
from .base import Candidate
from .base import Constraint
from .base import Requirement
from .candidates import REQUIRES_PYTHON_IDENTIFIER
from .factory import Factory
@ -46,15 +48,15 @@ else:
# services to those objects (access to pip's finder and preparer).
D = TypeVar("D")
V = TypeVar("V")
D = TypeVar('D')
V = TypeVar('V')
def _get_with_identifier(
mapping: Mapping[str, V],
identifier: str,
default: D,
) -> Union[D, V]:
) -> D | V:
"""Get item from a package name lookup mapping with a resolver identifier.
This extra logic is needed when the target mapping is keyed by package
@ -69,7 +71,7 @@ def _get_with_identifier(
# some regular expression. But since pip's resolver only spits out three
# kinds of identifiers: normalized PEP 503 names, normalized names plus
# extras, and Requires-Python, we can cheat a bit here.
name, open_bracket, _ = identifier.partition("[")
name, open_bracket, _ = identifier.partition('[')
if open_bracket and name in mapping:
return mapping[name]
return default
@ -89,19 +91,19 @@ class PipProvider(_ProviderBase):
def __init__(
self,
factory: Factory,
constraints: Dict[str, Constraint],
constraints: dict[str, Constraint],
ignore_dependencies: bool,
upgrade_strategy: str,
user_requested: Dict[str, int],
user_requested: dict[str, int],
) -> None:
self._factory = factory
self._constraints = constraints
self._ignore_dependencies = ignore_dependencies
self._upgrade_strategy = upgrade_strategy
self._user_requested = user_requested
self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf)
self._known_depths: dict[str, float] = collections.defaultdict(lambda: math.inf)
def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
def identify(self, requirement_or_candidate: Requirement | Candidate) -> str:
return requirement_or_candidate.name
def get_preference( # type: ignore
@ -109,9 +111,9 @@ class PipProvider(_ProviderBase):
identifier: str,
resolutions: Mapping[str, Candidate],
candidates: Mapping[str, Iterator[Candidate]],
information: Mapping[str, Iterable["PreferenceInformation"]],
backtrack_causes: Sequence["PreferenceInformation"],
) -> "Preference":
information: Mapping[str, Iterable[PreferenceInformation]],
backtrack_causes: Sequence[PreferenceInformation],
) -> Preference:
"""Produce a sort key for given requirement based on preference.
The lower the return value is, the more preferred this group of
@ -139,11 +141,11 @@ class PipProvider(_ProviderBase):
]
direct = candidate is not None
pinned = any(op[:2] == "==" for op in operators)
pinned = any(op[:2] == '==' for op in operators)
unfree = bool(operators)
try:
requested_order: Union[int, float] = self._user_requested[identifier]
requested_order: int | float = self._user_requested[identifier]
except KeyError:
requested_order = math.inf
parent_depths = (
@ -169,7 +171,7 @@ class PipProvider(_ProviderBase):
# delaying Setuptools helps reduce branches the resolver has to check.
# This serves as a temporary fix for issues like "apache-airflow[all]"
# while we work on "proper" branch pruning techniques.
delay_this = identifier == "setuptools"
delay_this = identifier == 'setuptools'
# Prefer the causes of backtracking on the assumption that the problem
# resolving the dependency tree is related to the failures that caused
@ -205,9 +207,9 @@ class PipProvider(_ProviderBase):
an upgrade strategy of "to-satisfy-only" means that `--upgrade`
was not specified).
"""
if self._upgrade_strategy == "eager":
if self._upgrade_strategy == 'eager':
return True
elif self._upgrade_strategy == "only-if-needed":
elif self._upgrade_strategy == 'only-if-needed':
user_order = _get_with_identifier(
self._user_requested,
identifier,
@ -238,7 +240,7 @@ class PipProvider(_ProviderBase):
@staticmethod
def is_backtrack_cause(
identifier: str, backtrack_causes: Sequence["PreferenceInformation"]
identifier: str, backtrack_causes: Sequence[PreferenceInformation],
) -> bool:
for backtrack_cause in backtrack_causes:
if identifier == backtrack_cause.requirement.name:

View file

@ -1,10 +1,14 @@
from __future__ import annotations
from collections import defaultdict
from logging import getLogger
from typing import Any, DefaultDict
from typing import Any
from typing import DefaultDict
from pip._vendor.resolvelib.reporters import BaseReporter
from .base import Candidate, Requirement
from .base import Candidate
from .base import Requirement
logger = getLogger(__name__)
@ -15,20 +19,20 @@ class PipReporter(BaseReporter):
self._messages_at_backtrack = {
1: (
"pip is looking at multiple versions of {package_name} to "
"determine which version is compatible with other "
"requirements. This could take a while."
'pip is looking at multiple versions of {package_name} to '
'determine which version is compatible with other '
'requirements. This could take a while.'
),
8: (
"pip is looking at multiple versions of {package_name} to "
"determine which version is compatible with other "
"requirements. This could take a while."
'pip is looking at multiple versions of {package_name} to '
'determine which version is compatible with other '
'requirements. This could take a while.'
),
13: (
"This is taking longer than usual. You might need to provide "
"the dependency resolver with stricter constraints to reduce "
"runtime. See https://pip.pypa.io/warnings/backtracking for "
"guidance. If you want to abort this run, press Ctrl + C."
'This is taking longer than usual. You might need to provide '
'the dependency resolver with stricter constraints to reduce '
'runtime. See https://pip.pypa.io/warnings/backtracking for '
'guidance. If you want to abort this run, press Ctrl + C.'
),
}
@ -40,29 +44,29 @@ class PipReporter(BaseReporter):
return
message = self._messages_at_backtrack[count]
logger.info("INFO: %s", message.format(package_name=candidate.name))
logger.info('INFO: %s', message.format(package_name=candidate.name))
class PipDebuggingReporter(BaseReporter):
"""A reporter that does an info log for every event it sees."""
def starting(self) -> None:
logger.info("Reporter.starting()")
logger.info('Reporter.starting()')
def starting_round(self, index: int) -> None:
logger.info("Reporter.starting_round(%r)", index)
logger.info('Reporter.starting_round(%r)', index)
def ending_round(self, index: int, state: Any) -> None:
logger.info("Reporter.ending_round(%r, state)", index)
logger.info('Reporter.ending_round(%r, state)', index)
def ending(self, state: Any) -> None:
logger.info("Reporter.ending(%r)", state)
logger.info('Reporter.ending(%r)', state)
def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None:
logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent)
logger.info('Reporter.adding_requirement(%r, %r)', requirement, parent)
def backtracking(self, candidate: Candidate) -> None:
logger.info("Reporter.backtracking(%r)", candidate)
logger.info('Reporter.backtracking(%r)', candidate)
def pinning(self, candidate: Candidate) -> None:
logger.info("Reporter.pinning(%r)", candidate)
logger.info('Reporter.pinning(%r)', candidate)

View file

@ -1,9 +1,14 @@
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from __future__ import annotations
from pip._internal.req.req_install import InstallRequirement
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.utils import NormalizedName
from .base import Candidate, CandidateLookup, Requirement, format_name
from .base import Candidate
from .base import CandidateLookup
from .base import format_name
from .base import Requirement
class ExplicitRequirement(Requirement):
@ -14,7 +19,7 @@ class ExplicitRequirement(Requirement):
return str(self.candidate)
def __repr__(self) -> str:
return "{class_name}({candidate!r})".format(
return '{class_name}({candidate!r})'.format(
class_name=self.__class__.__name__,
candidate=self.candidate,
)
@ -41,7 +46,7 @@ class ExplicitRequirement(Requirement):
class SpecifierRequirement(Requirement):
def __init__(self, ireq: InstallRequirement) -> None:
assert ireq.link is None, "This is a link, not a specifier"
assert ireq.link is None, 'This is a link, not a specifier'
self._ireq = ireq
self._extras = frozenset(ireq.extras)
@ -49,14 +54,14 @@ class SpecifierRequirement(Requirement):
return str(self._ireq.req)
def __repr__(self) -> str:
return "{class_name}({requirement!r})".format(
return '{class_name}({requirement!r})'.format(
class_name=self.__class__.__name__,
requirement=str(self._ireq.req),
)
@property
def project_name(self) -> NormalizedName:
assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
assert self._ireq.req, 'Specifier-backed ireq is always PEP 508'
return canonicalize_name(self._ireq.req.name)
@property
@ -69,26 +74,26 @@ class SpecifierRequirement(Requirement):
# 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(",")]
parts = [s.strip() for s in str(self).split(',')]
if len(parts) == 0:
return ""
return ''
elif len(parts) == 1:
return parts[0]
return ", ".join(parts[:-1]) + " and " + parts[-1]
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}"
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"
assert self._ireq.req, 'Specifier-backed ireq is always PEP 508'
spec = self._ireq.req.specifier
return spec.contains(candidate.version, prereleases=True)
@ -101,10 +106,10 @@ class RequiresPythonRequirement(Requirement):
self._candidate = match
def __str__(self) -> str:
return f"Python {self.specifier}"
return f'Python {self.specifier}'
def __repr__(self) -> str:
return "{class_name}({specifier!r})".format(
return '{class_name}({specifier!r})'.format(
class_name=self.__class__.__name__,
specifier=str(self.specifier),
)
@ -126,7 +131,7 @@ class RequiresPythonRequirement(Requirement):
return None, None
def is_satisfied_by(self, candidate: Candidate) -> bool:
assert candidate.name == self._candidate.name, "Not Python candidate"
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.
@ -140,10 +145,10 @@ class UnsatisfiableRequirement(Requirement):
self._name = name
def __str__(self) -> str:
return f"{self._name} (unavailable)"
return f'{self._name} (unavailable)'
def __repr__(self) -> str:
return "{class_name}({name!r})".format(
return '{class_name}({name!r})'.format(
class_name=self.__class__.__name__,
name=str(self._name),
)

View file

@ -1,26 +1,34 @@
from __future__ import annotations
import functools
import logging
import os
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible
from pip._vendor.resolvelib import Resolver as RLResolver
from pip._vendor.resolvelib.structs import DirectedGraph
from typing import cast
from typing import Dict
from typing import List
from typing import Optional
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
from pip._internal.cache import WheelCache
from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
from pip._internal.resolution.base import BaseResolver
from pip._internal.resolution.base import InstallRequirementProvider
from pip._internal.resolution.resolvelib.provider import PipProvider
from pip._internal.resolution.resolvelib.reporter import (
PipDebuggingReporter,
PipReporter,
)
from pip._internal.resolution.resolvelib.reporter import PipDebuggingReporter
from pip._internal.resolution.resolvelib.reporter import PipReporter
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.resolvelib import BaseReporter
from pip._vendor.resolvelib import ResolutionImpossible
from pip._vendor.resolvelib import Resolver as RLResolver
from pip._vendor.resolvelib.structs import DirectedGraph
from .base import Candidate, Requirement
from .base import Candidate
from .base import Requirement
from .factory import Factory
if TYPE_CHECKING:
@ -33,13 +41,13 @@ logger = logging.getLogger(__name__)
class Resolver(BaseResolver):
_allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"}
_allowed_strategies = {'eager', 'only-if-needed', 'to-satisfy-only'}
def __init__(
self,
preparer: RequirementPreparer,
finder: PackageFinder,
wheel_cache: Optional[WheelCache],
wheel_cache: WheelCache | None,
make_install_req: InstallRequirementProvider,
use_user_site: bool,
ignore_dependencies: bool,
@ -48,7 +56,7 @@ class Resolver(BaseResolver):
force_reinstall: bool,
upgrade_strategy: str,
suppress_build_failures: bool,
py_version_info: Optional[Tuple[int, ...]] = None,
py_version_info: tuple[int, ...] | None = None,
):
super().__init__()
assert upgrade_strategy in self._allowed_strategies
@ -67,10 +75,10 @@ class Resolver(BaseResolver):
)
self.ignore_dependencies = ignore_dependencies
self.upgrade_strategy = upgrade_strategy
self._result: Optional[Result] = None
self._result: Result | None = None
def resolve(
self, root_reqs: List[InstallRequirement], check_supported_wheels: bool
self, root_reqs: list[InstallRequirement], check_supported_wheels: bool,
) -> RequirementSet:
collected = self.factory.collect_root_requirements(root_reqs)
provider = PipProvider(
@ -80,7 +88,7 @@ class Resolver(BaseResolver):
upgrade_strategy=self.upgrade_strategy,
user_requested=collected.user_requested,
)
if "PIP_RESOLVER_DEBUG" in os.environ:
if 'PIP_RESOLVER_DEBUG' in os.environ:
reporter: BaseReporter = PipDebuggingReporter()
else:
reporter = PipReporter()
@ -92,12 +100,12 @@ class Resolver(BaseResolver):
try:
try_to_avoid_resolution_too_deep = 2000000
result = self._result = resolver.resolve(
collected.requirements, max_rounds=try_to_avoid_resolution_too_deep
collected.requirements, max_rounds=try_to_avoid_resolution_too_deep,
)
except ResolutionImpossible as e:
error = self.factory.get_installation_error(
cast("ResolutionImpossible[Requirement, Candidate]", e),
cast('ResolutionImpossible[Requirement, Candidate]', e),
collected.constraints,
)
raise error from e
@ -129,9 +137,9 @@ class Resolver(BaseResolver):
if candidate.source_link.is_wheel:
# is a local wheel -- do nothing.
logger.info(
"%s is already installed with the same version as the "
"provided wheel. Use --force-reinstall to force an "
"installation of the wheel.",
'%s is already installed with the same version as the '
'provided wheel. Use --force-reinstall to force an '
'installation of the wheel.',
ireq.name,
)
continue
@ -146,14 +154,14 @@ class Resolver(BaseResolver):
# The reason can contain non-ASCII characters, Unicode
# is required for Python 2.
msg = (
"The candidate selected for download or install is a "
"yanked version: {name!r} candidate (version {version} "
"at {link})\nReason for being yanked: {reason}"
'The candidate selected for download or install is a '
'yanked version: {name!r} candidate (version {version} '
'at {link})\nReason for being yanked: {reason}'
).format(
name=candidate.name,
version=candidate.version,
link=link,
reason=link.yanked_reason or "<none given>",
reason=link.yanked_reason or '<none given>',
)
logger.warning(msg)
@ -164,8 +172,8 @@ class Resolver(BaseResolver):
return req_set
def get_installation_order(
self, req_set: RequirementSet
) -> List[InstallRequirement]:
self, req_set: RequirementSet,
) -> list[InstallRequirement]:
"""Get order for installation of requirements in RequirementSet.
The returned list contains a requirement before another that depends on
@ -178,7 +186,7 @@ class Resolver(BaseResolver):
arbitrary points. We make no guarantees about where the cycle
would be broken, other than it *would* be broken.
"""
assert self._result is not None, "must call resolve() first"
assert self._result is not None, 'must call resolve() first'
if not req_set.requirements:
# Nothing is left to install, so we do not need an order.
@ -196,8 +204,8 @@ class Resolver(BaseResolver):
def get_topological_weights(
graph: "DirectedGraph[Optional[str]]", requirement_keys: Set[str]
) -> Dict[Optional[str], int]:
graph: DirectedGraph[Optional[str]], requirement_keys: set[str],
) -> dict[str | None, int]:
"""Assign weights to each node based on how "deep" they are.
This implementation may change at any point in the future without prior
@ -223,10 +231,10 @@ def get_topological_weights(
We are only interested in the weights of packages that are in the
requirement_keys.
"""
path: Set[Optional[str]] = set()
weights: Dict[Optional[str], int] = {}
path: set[str | None] = set()
weights: dict[str | None, int] = {}
def visit(node: Optional[str]) -> None:
def visit(node: str | None) -> None:
if node in path:
# We hit a cycle, so we'll break it here.
return
@ -285,9 +293,9 @@ def get_topological_weights(
def _req_set_item_sorter(
item: Tuple[str, InstallRequirement],
weights: Dict[Optional[str], int],
) -> Tuple[int, str]:
item: tuple[str, InstallRequirement],
weights: dict[str | None, int],
) -> tuple[int, str]:
"""Key function used to sort install requirements for installation.
Based on the "weight" mapping calculated in ``get_installation_order()``.