3from typing
import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast
10 InstallationSubprocessError,
17 install_req_from_editable,
18 install_req_from_line,
24from .base
import Candidate, CandidateVersion, Requirement, format_name
27 from .factory
import Factory
32 "AlreadyInstalledCandidate",
38REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName,
"<Python from Requires-Python>")
41def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]:
42 """The runtime version of BaseCandidate."""
43 base_candidate_classes = (
44 AlreadyInstalledCandidate,
48 if isinstance(candidate, base_candidate_classes):
54 link: Link, template: InstallRequirement
55) -> InstallRequirement:
61 ireq = install_req_from_line(
79 link: Link, template: InstallRequirement
80) -> InstallRequirement:
82 ireq = install_req_from_editable(
99 dist: BaseDistribution, template: InstallRequirement
100) -> InstallRequirement:
104 line = f
"{dist.canonical_name} @ {template.link.url}"
106 line = f
"{dist.canonical_name}=={dist.version}"
107 ireq = install_req_from_line(
123 """A candidate backed by an ``InstallRequirement``.
125 This represents a package request with the target not being already
126 in the environment, and needs to be fetched and installed. The backing
127 ``InstallRequirement`` is responsible for most of the leg work; this
128 class exposes appropriate information to the resolver.
130 :param link: The link passed to the ``InstallRequirement``. The backing
131 ``InstallRequirement`` will use this link to fetch the distribution.
132 :param source_link: The link this candidate "originates" from. This is
133 different from ``link`` when the link is found in the wheel cache.
134 ``link`` would point to the wheel cache, while this points to the
135 found remote link (e.g. from pypi.org).
138 dist: BaseDistribution
145 ireq: InstallRequirement,
147 name: Optional[NormalizedName] =
None,
148 version: Optional[CandidateVersion] =
None,
159 return f
"{self.name} {self.version}"
162 return "{class_name}({link!r})".format(
164 link=str(self.
_link),
181 """The normalised name of the project the candidate refers to"""
182 if self.
_name is None:
197 return "{} {} (from {})".format(
207 """Check for consistency of project name and version of dist."""
226 except HashError
as e:
232 except InstallationSubprocessError
as exc:
243 yield self.
_factory.make_requirement_from_spec(str(r), self.
_ireq)
244 yield self.
_factory.make_requires_python_requirement(self.
distdist.requires_python)
256 template: InstallRequirement,
258 name: Optional[NormalizedName] =
None,
259 version: Optional[CandidateVersion] =
None,
263 if cache_entry
is not None:
271 assert name == wheel_name, f
"{name!r} != {wheel_name!r} for wheel"
273 if version
is not None:
275 assert version == wheel_version,
"{!r} != {!r} for wheel {}".format(
276 version, wheel_version, name
279 if cache_entry
is not None:
295 source_link=source_link,
313 template: InstallRequirement,
315 name: Optional[NormalizedName] =
None,
316 version: Optional[CandidateVersion] =
None,
337 dist: BaseDistribution,
338 template: InstallRequirement,
350 skip_reason =
"already satisfied"
354 return str(self.
dist)
357 return "{class_name}({distribution!r})".format(
359 distribution=self.
dist,
372 return self.
dist.canonical_name
386 return self.
dist.editable
389 return f
"{self.name} {self.version} (Installed)"
392 if not with_requires:
395 yield self.
_factory.make_requirement_from_spec(str(r), self.
_ireq)
402 """A candidate that has 'extras', indicating additional dependencies.
404 Requirements can be for a project with dependencies, something like
405 foo[extra]. The extras don't affect the project/version being installed
406 directly, but indicate that we need additional dependencies. We model that
407 by having an artificial ExtrasCandidate that wraps the "base" candidate.
409 The ExtrasCandidate differs from the base in the following ways:
411 1. It has a unique name, of the form foo[extra]. This causes the resolver
412 to treat it as a separate node in the dependency graph.
413 2. When we're getting the candidate's dependencies,
414 a) We specify that we want the extra dependencies as well.
415 b) We add a dependency on the base candidate.
416 See below for why this is needed.
417 3. We return None for the underlying InstallRequirement, as the base
418 candidate will provide it, and we don't want to end up with duplicates.
420 The dependency on the base candidate is needed so that the resolver can't
421 decide that it should recommend foo[extra1] version 1.0 and foo[extra2]
422 version 2.0. Having those candidates depend on foo=1.0 and foo=2.0
423 respectively forces the resolver to recognise that this is a conflict.
429 extras: FrozenSet[str],
435 name, rest = str(self.
base).split(
" ", 1)
436 return "{}[{}] {}".format(name,
",".join(self.
extras), rest)
439 return "{class_name}(base={base!r}, extras={extras!r})".format(
455 return self.
base.project_name
458 def name(self) -> str:
459 """The normalised name of the project the candidate refers to"""
460 return format_name(self.
base.project_name, self.
extras)
464 return self.
base.version
467 return "{} [{}]".format(
473 return self.
base.is_installed
477 return self.
base.is_editable
481 return self.
base.source_link
484 factory = self.
base._factory
489 if not with_requires:
496 for extra
in sorted(invalid_extras):
498 "%s %s does not provide the extra '%s'",
506 str(r), self.
base._ireq, valid_extras
522 def __init__(self, py_version_info: Optional[Tuple[int, ...]]) ->
None:
523 if py_version_info
is not None:
524 version_info = normalize_version_info(py_version_info)
534 return f
"Python {self._version}"
538 return REQUIRES_PYTHON_IDENTIFIER
541 def name(self) -> str:
542 return REQUIRES_PYTHON_IDENTIFIER
545 def version(self) -> CandidateVersion:
549 return f
"Python {self.version}"
NormalizedName project_name(self)
CandidateVersion version(self)
None __init__(self, BaseDistribution dist, InstallRequirement template, "Factory" factory)
Optional[InstallRequirement] get_install_requirement(self)
Iterable[Optional[Requirement]] iter_dependencies(self, bool with_requires)
str format_for_error(self)
NormalizedName project_name(self)
CandidateVersion version(self)
bool __eq__(self, Any other)
None __init__(self, Link link, InstallRequirement template, "Factory" factory, Optional[NormalizedName] name=None, Optional[CandidateVersion] version=None)
BaseDistribution _prepare_distribution(self)
None __init__(self, BaseCandidate base, FrozenSet[str] extras)
Optional[InstallRequirement] get_install_requirement(self)
Iterable[Optional[Requirement]] iter_dependencies(self, bool with_requires)
str format_for_error(self)
NormalizedName project_name(self)
Optional[Link] source_link(self)
CandidateVersion version(self)
bool __eq__(self, Any other)
None __init__(self, Link link, InstallRequirement template, "Factory" factory, Optional[NormalizedName] name=None, Optional[CandidateVersion] version=None)
BaseDistribution _prepare_distribution(self)
Optional[InstallRequirement] get_install_requirement(self)
Iterable[Optional[Requirement]] iter_dependencies(self, bool with_requires)
None __init__(self, Optional[Tuple[int,...]] py_version_info)
str format_for_error(self)
NormalizedName project_name(self)
Optional[InstallRequirement] get_install_requirement(self)
Iterable[Optional[Requirement]] iter_dependencies(self, bool with_requires)
str format_for_error(self)
NormalizedName project_name(self)
BaseDistribution _prepare(self)
None _check_metadata_consistency(self, BaseDistribution dist)
None __init__(self, Link link, Link source_link, InstallRequirement ireq, "Factory" factory, Optional[NormalizedName] name=None, Optional[CandidateVersion] version=None)
Optional[Link] source_link(self)
BaseDistribution _prepare_distribution(self)
CandidateVersion version(self)
bool __eq__(self, Any other)
InstallRequirement make_install_req_from_editable(Link link, InstallRequirement template)
InstallRequirement _make_install_req_from_dist(BaseDistribution dist, InstallRequirement template)
InstallRequirement make_install_req_from_link(Link link, InstallRequirement template)