21 from pip._vendor.packaging.requirements import InvalidRequirement
22 from pip._vendor.packaging.specifiers import SpecifierSet
23 from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
24 from pip._vendor.resolvelib import ResolutionImpossible
26 from pip._internal.cache import CacheEntry, WheelCache
27 from pip._internal.exceptions import (
31 UnsupportedPythonVersion,
34 from pip._internal.index.package_finder import PackageFinder
35 from pip._internal.metadata import BaseDistribution, get_default_environment
36 from pip._internal.models.link import Link
37 from pip._internal.models.wheel import Wheel
38 from pip._internal.operations.prepare import RequirementPreparer
39 from pip._internal.req.constructors import install_req_from_link_and_ireq
40 from pip._internal.req.req_install import (
42 check_invalid_constraint_type,
44 from pip._internal.resolution.base import InstallRequirementProvider
45 from pip._internal.utils.compatibility_tags import get_supported
46 from pip._internal.utils.hashes import Hashes
47 from pip._internal.utils.packaging import get_requirement
48 from pip._internal.utils.virtualenv import running_under_virtualenv
50 from .base import Candidate, CandidateVersion, Constraint, Requirement
51 from .candidates import (
52 AlreadyInstalledCandidate,
57 RequiresPythonCandidate,
60 from .found_candidates import FoundCandidates, IndexCandidateInfo
61 from .requirements import (
63 RequiresPythonRequirement,
65 UnsatisfiableRequirement,
69 from typing import Protocol
71 class ConflictCause(Protocol):
72 requirement: RequiresPythonRequirement
76 logger = logging.getLogger(__name__)
82 class CollectedRootRequirements(NamedTuple):
83 requirements: List[Requirement]
84 constraints: Dict[str, Constraint]
85 user_requested: Dict[str, int]
91 finder: PackageFinder,
92 preparer: RequirementPreparer,
93 make_install_req: InstallRequirementProvider,
94 wheel_cache: Optional[WheelCache],
96 force_reinstall: bool,
97 ignore_installed: bool,
98 ignore_requires_python: bool,
99 py_version_info: Optional[Tuple[int, ...]] = None,
101 self._finder = finder
102 self.preparer = preparer
103 self._wheel_cache = wheel_cache
104 self._python_candidate = RequiresPythonCandidate(py_version_info)
105 self._make_install_req_from_spec = make_install_req
106 self._use_user_site = use_user_site
107 self._force_reinstall = force_reinstall
108 self._ignore_requires_python = ignore_requires_python
110 self._build_failures: Cache[InstallationError] = {}
111 self._link_candidate_cache: Cache[LinkCandidate] = {}
112 self._editable_candidate_cache: Cache[EditableCandidate] = {}
113 self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {}
114 self._extras_candidate_cache: Dict[
115 Tuple[int, FrozenSet[str]], ExtrasCandidate
118 if not ignore_installed:
119 env = get_default_environment()
120 self._installed_dists = {
121 dist.canonical_name: dist
122 for dist in env.iter_installed_distributions(local_only=False)
125 self._installed_dists = {}
128 def force_reinstall(self) -> bool:
129 return self._force_reinstall
131 def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
132 if not link.is_wheel:
134 wheel = Wheel(link.filename)
135 if wheel.supported(self._finder.target_python.get_tags()):
137 msg = f"{link.filename} is not a supported wheel on this platform."
138 raise UnsupportedWheel(msg)
140 def _make_extras_candidate(
141 self, base: BaseCandidate, extras: FrozenSet[str]
142 ) -> ExtrasCandidate:
143 cache_key = (id(base), extras)
145 candidate = self._extras_candidate_cache[cache_key]
147 candidate = ExtrasCandidate(base, extras)
148 self._extras_candidate_cache[cache_key] = candidate
151 def _make_candidate_from_dist(
153 dist: BaseDistribution,
154 extras: FrozenSet[str],
155 template: InstallRequirement,
158 base = self._installed_candidate_cache[dist.canonical_name]
160 base = AlreadyInstalledCandidate(dist, template, factory=self)
161 self._installed_candidate_cache[dist.canonical_name] = base
164 return self._make_extras_candidate(base, extras)
166 def _make_candidate_from_link(
169 extras: FrozenSet[str],
170 template: InstallRequirement,
171 name: Optional[NormalizedName],
172 version: Optional[CandidateVersion],
173 ) -> Optional[Candidate]:
174 # TODO: Check already installed candidate, and use it if the link and
175 # editable flag match.
177 if link in self._build_failures:
178 # We already tried this candidate before, and it does not build.
179 # Don't bother trying again.
182 if template.editable:
183 if link not in self._editable_candidate_cache:
185 self._editable_candidate_cache[link] = EditableCandidate(
192 except MetadataInconsistent as e:
194 "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
197 extra={"markup": True},
199 self._build_failures[link] = e
202 base: BaseCandidate = self._editable_candidate_cache[link]
204 if link not in self._link_candidate_cache:
206 self._link_candidate_cache[link] = LinkCandidate(
213 except MetadataInconsistent as e:
215 "Discarding [blue underline]%s[/]: [yellow]%s[reset]",
218 extra={"markup": True},
220 self._build_failures[link] = e
222 base = self._link_candidate_cache[link]
226 return self._make_extras_candidate(base, extras)
228 def _iter_found_candidates(
230 ireqs: Sequence[InstallRequirement],
231 specifier: SpecifierSet,
233 prefers_installed: bool,
234 incompatible_ids: Set[int],
235 ) -> Iterable[Candidate]:
239 # The InstallRequirement implementation requires us to give it a
240 # "template". Here we just choose the first requirement to represent
242 # Hopefully the Project model can correct this mismatch in the future.
244 assert template.req, "Candidates found on index must be PEP 508"
245 name = canonicalize_name(template.req.name)
247 extras: FrozenSet[str] = frozenset()
249 assert ireq.req, "Candidates found on index must be PEP 508"
250 specifier &= ireq.req.specifier
251 hashes &= ireq.hashes(trust_internet=False)
252 extras |= frozenset(ireq.extras)
254 def _get_installed_candidate() -> Optional[Candidate]:
255 """Get the candidate for the currently-installed version."""
256 # If --force-reinstall is set, we want the version from the index
257 # instead, so we "pretend" there is nothing installed.
258 if self._force_reinstall:
261 installed_dist = self._installed_dists[name]
264 # Don't use the installed distribution if its version does not fit
265 # the current dependency graph.
266 if not specifier.contains(installed_dist.version, prereleases=True):
268 candidate = self._make_candidate_from_dist(
273 # The candidate is a known incompatibility. Don't use it.
274 if id(candidate) in incompatible_ids:
278 def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]:
279 result = self._finder.find_best_candidate(
284 icans = list(result.iter_applicable())
286 # PEP 592: Yanked releases are ignored unless the specifier
287 # explicitly pins a version (via '==' or '===') that can be
288 # solely satisfied by a yanked release.
289 all_yanked = all(ican.link.is_yanked for ican in icans)
291 def is_pinned(specifier: SpecifierSet) -> bool:
293 if sp.operator == "===":
295 if sp.operator != "==":
297 if sp.version.endswith(".*"):
302 pinned = is_pinned(specifier)
304 # PackageFinder returns earlier versions first, so we reverse.
305 for ican in reversed(icans):
306 if not (all_yanked and pinned) and ican.link.is_yanked:
308 func = functools.partial(
309 self._make_candidate_from_link,
314 version=ican.version,
316 yield ican.version, func
318 return FoundCandidates(
319 iter_index_candidate_infos,
320 _get_installed_candidate(),
325 def _iter_explicit_candidates_from_base(
327 base_requirements: Iterable[Requirement],
328 extras: FrozenSet[str],
329 ) -> Iterator[Candidate]:
330 """Produce explicit candidates from the base given an extra-ed package.
332 :param base_requirements: Requirements known to the resolver. The
333 requirements are guaranteed to not have extras.
334 :param extras: The extras to inject into the explicit requirements'
337 for req in base_requirements:
338 lookup_cand, _ = req.get_candidate_lookup()
339 if lookup_cand is None: # Not explicit.
341 # We've stripped extras from the identifier, and should always
342 # get a BaseCandidate here, unless there's a bug elsewhere.
343 base_cand = as_base_candidate(lookup_cand)
344 assert base_cand is not None, "no extras here"
345 yield self._make_extras_candidate(base_cand, extras)
347 def _iter_candidates_from_constraints(
350 constraint: Constraint,
351 template: InstallRequirement,
352 ) -> Iterator[Candidate]:
353 """Produce explicit candidates from constraints.
355 This creates "fake" InstallRequirement objects that are basically clones
356 of what "should" be the template, but with original_link set to link.
358 for link in constraint.links:
359 self._fail_if_link_is_unsupported_wheel(link)
360 candidate = self._make_candidate_from_link(
363 template=install_req_from_link_and_ireq(link, template),
364 name=canonicalize_name(identifier),
373 requirements: Mapping[str, Iterable[Requirement]],
374 incompatibilities: Mapping[str, Iterator[Candidate]],
375 constraint: Constraint,
376 prefers_installed: bool,
377 ) -> Iterable[Candidate]:
378 # Collect basic lookup information from the requirements.
379 explicit_candidates: Set[Candidate] = set()
380 ireqs: List[InstallRequirement] = []
381 for req in requirements[identifier]:
382 cand, ireq = req.get_candidate_lookup()
384 explicit_candidates.add(cand)
388 # If the current identifier contains extras, add explicit candidates
389 # from entries from extra-less identifier.
390 with contextlib.suppress(InvalidRequirement):
391 parsed_requirement = get_requirement(identifier)
392 explicit_candidates.update(
393 self._iter_explicit_candidates_from_base(
394 requirements.get(parsed_requirement.name, ()),
395 frozenset(parsed_requirement.extras),
399 # Add explicit candidates from constraints. We only do this if there are
400 # known ireqs, which represent requirements not already explicit. If
401 # there are no ireqs, we're constraining already-explicit requirements,
402 # which is handled later when we return the explicit candidates.
405 explicit_candidates.update(
406 self._iter_candidates_from_constraints(
412 except UnsupportedWheel:
413 # If we're constrained to install a wheel incompatible with the
414 # target architecture, no candidates will ever be valid.
417 # Since we cache all the candidates, incompatibility identification
418 # can be made quicker by comparing only the id() values.
419 incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
421 # If none of the requirements want an explicit candidate, we can ask
422 # the finder for candidates.
423 if not explicit_candidates:
424 return self._iter_found_candidates(
426 constraint.specifier,
434 for c in explicit_candidates
435 if id(c) not in incompat_ids
436 and constraint.is_satisfied_by(c)
437 and all(req.is_satisfied_by(c) for req in requirements[identifier])
440 def _make_requirement_from_install_req(
441 self, ireq: InstallRequirement, requested_extras: Iterable[str]
442 ) -> Optional[Requirement]:
443 if not ireq.match_markers(requested_extras):
445 "Ignoring %s: markers '%s' don't match your environment",
451 return SpecifierRequirement(ireq)
452 self._fail_if_link_is_unsupported_wheel(ireq.link)
453 cand = self._make_candidate_from_link(
455 extras=frozenset(ireq.extras),
457 name=canonicalize_name(ireq.name) if ireq.name else None,
461 # There's no way we can satisfy a URL requirement if the underlying
462 # candidate fails to build. An unnamed URL must be user-supplied, so
463 # we fail eagerly. If the URL is named, an unsatisfiable requirement
464 # can make the resolver do the right thing, either backtrack (and
465 # maybe find some other requirement that's buildable) or raise a
466 # ResolutionImpossible eventually.
468 raise self._build_failures[ireq.link]
469 return UnsatisfiableRequirement(canonicalize_name(ireq.name))
470 return self.make_requirement_from_candidate(cand)
472 def collect_root_requirements(
473 self, root_ireqs: List[InstallRequirement]
474 ) -> CollectedRootRequirements:
475 collected = CollectedRootRequirements([], {}, {})
476 for i, ireq in enumerate(root_ireqs):
478 # Ensure we only accept valid constraints
479 problem = check_invalid_constraint_type(ireq)
481 raise InstallationError(problem)
482 if not ireq.match_markers():
484 assert ireq.name, "Constraint must be named"
485 name = canonicalize_name(ireq.name)
486 if name in collected.constraints:
487 collected.constraints[name] &= ireq
489 collected.constraints[name] = Constraint.from_ireq(ireq)
491 req = self._make_requirement_from_install_req(
497 if ireq.user_supplied and req.name not in collected.user_requested:
498 collected.user_requested[req.name] = i
499 collected.requirements.append(req)
502 def make_requirement_from_candidate(
503 self, candidate: Candidate
504 ) -> ExplicitRequirement:
505 return ExplicitRequirement(candidate)
507 def make_requirement_from_spec(
510 comes_from: Optional[InstallRequirement],
511 requested_extras: Iterable[str] = (),
512 ) -> Optional[Requirement]:
513 ireq = self._make_install_req_from_spec(specifier, comes_from)
514 return self._make_requirement_from_install_req(ireq, requested_extras)
516 def make_requires_python_requirement(
518 specifier: SpecifierSet,
519 ) -> Optional[Requirement]:
520 if self._ignore_requires_python:
522 # Don't bother creating a dependency for an empty Requires-Python.
523 if not str(specifier):
525 return RequiresPythonRequirement(specifier, self._python_candidate)
527 def get_wheel_cache_entry(
528 self, link: Link, name: Optional[str]
529 ) -> Optional[CacheEntry]:
530 """Look up the link in the wheel cache.
532 If ``preparer.require_hashes`` is True, don't use the wheel cache,
533 because cached wheels, always built locally, have different hashes
534 than the files downloaded from the index server and thus throw false
535 hash mismatches. Furthermore, cached wheels at present have
536 nondeterministic contents due to file modification times.
538 if self._wheel_cache is None or self.preparer.require_hashes:
540 return self._wheel_cache.get_cache_entry(
543 supported_tags=get_supported(),
546 def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]:
547 # TODO: Are there more cases this needs to return True? Editable?
548 dist = self._installed_dists.get(candidate.project_name)
549 if dist is None: # Not installed, no uninstallation required.
552 # We're installing into global site. The current installation must
553 # be uninstalled, no matter it's in global or user site, because the
554 # user site installation has precedence over global.
555 if not self._use_user_site:
558 # We're installing into user site. Remove the user site installation.
562 # We're installing into user site, but the installed incompatible
563 # package is in global site. We can't uninstall that, and would let
564 # the new user installation to "shadow" it. But shadowing won't work
565 # in virtual environments, so we error out.
566 if running_under_virtualenv() and dist.in_site_packages:
568 f"Will not install to the user site because it will lack "
569 f"sys.path precedence to {dist.raw_name} in {dist.location}"
571 raise InstallationError(message)
574 def _report_requires_python_error(
575 self, causes: Sequence["ConflictCause"]
576 ) -> UnsupportedPythonVersion:
577 assert causes, "Requires-Python error reported with no cause"
579 version = self._python_candidate.version
582 specifier = str(causes[0].requirement.specifier)
584 f"Package {causes[0].parent.name!r} requires a different "
585 f"Python: {version} not in {specifier!r}"
587 return UnsupportedPythonVersion(message)
589 message = f"Packages require a different Python. {version} not in:"
591 package = cause.parent.format_for_error()
592 specifier = str(cause.requirement.specifier)
593 message += f"\n{specifier!r} (required by {package})"
594 return UnsupportedPythonVersion(message)
596 def _report_single_requirement_conflict(
597 self, req: Requirement, parent: Optional[Candidate]
598 ) -> DistributionNotFound:
602 req_disp = f"{req} (from {parent.name})"
604 cands = self._finder.find_all_candidates(req.project_name)
605 skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
606 versions = [str(v) for v in sorted({c.version for c in cands})]
608 if skipped_by_requires_python:
610 "Ignored the following versions that require a different python "
612 "; ".join(skipped_by_requires_python) or "none",
615 "Could not find a version that satisfies the requirement %s "
616 "(from versions: %s)",
618 ", ".join(versions) or "none",
620 if str(req) == "requirements.txt":
622 "HINT: You are attempting to install a package literally "
623 'named "requirements.txt" (which cannot exist). Consider '
624 "using the '-r' flag to install the packages listed in "
628 return DistributionNotFound(f"No matching distribution found for {req}")
630 def get_installation_error(
632 e: "ResolutionImpossible[Requirement, Candidate]",
633 constraints: Dict[str, Constraint],
634 ) -> InstallationError:
636 assert e.causes, "Installation error reported with no cause"
638 # If one of the things we can't solve is "we need Python X.Y",
639 # that is what we report.
640 requires_python_causes = [
642 for cause in e.causes
643 if isinstance(cause.requirement, RequiresPythonRequirement)
644 and not cause.requirement.is_satisfied_by(self._python_candidate)
646 if requires_python_causes:
647 # The comprehension above makes sure all Requirement instances are
648 # RequiresPythonRequirement, so let's cast for convenience.
649 return self._report_requires_python_error(
650 cast("Sequence[ConflictCause]", requires_python_causes),
653 # Otherwise, we have a set of causes which can't all be satisfied
656 # The simplest case is when we have *one* cause that can't be
657 # satisfied. We just report that case.
658 if len(e.causes) == 1:
659 req, parent = e.causes[0]
660 if req.name not in constraints:
661 return self._report_single_requirement_conflict(req, parent)
663 # OK, we now have a list of requirements that can't all be
666 # A couple of formatting helpers
667 def text_join(parts: List[str]) -> str:
671 return ", ".join(parts[:-1]) + " and " + parts[-1]
673 def describe_trigger(parent: Candidate) -> str:
674 ireq = parent.get_install_requirement()
675 if not ireq or not ireq.comes_from:
676 return f"{parent.name}=={parent.version}"
677 if isinstance(ireq.comes_from, InstallRequirement):
678 return str(ireq.comes_from.name)
679 return str(ireq.comes_from)
682 for req, parent in e.causes:
684 # This is a root requirement, so we can report it directly
685 trigger = req.format_for_error()
687 trigger = describe_trigger(parent)
688 triggers.add(trigger)
691 info = text_join(sorted(triggers))
693 info = "the requested packages"
696 "Cannot install {} because these package versions "
697 "have conflicting dependencies.".format(info)
700 msg = "\nThe conflict is caused by:"
702 relevant_constraints = set()
703 for req, parent in e.causes:
704 if req.name in constraints:
705 relevant_constraints.add(req.name)
708 msg = msg + f"{parent.name} {parent.version} depends on "
710 msg = msg + "The user requested "
711 msg = msg + req.format_for_error()
712 for key in relevant_constraints:
713 spec = constraints[key].specifier
714 msg += f"\n The user requested (constraint) {key}{spec}"
719 + "To fix this you could try to:\n"
720 + "1. loosen the range of package versions you've specified\n"
721 + "2. remove package versions to allow pip attempt to solve "
722 + "the dependency conflict\n"
727 return DistributionNotFound(
728 "ResolutionImpossible: for help visit "
729 "https://pip.pypa.io/en/latest/topics/dependency-resolution/"
730 "#dealing-with-dependency-conflicts"