Skip to content
1 change: 1 addition & 0 deletions news/13588.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
On ``ResolutionImpossible`` errors, include a note about causes with no candidates.
23 changes: 23 additions & 0 deletions src/pip/_internal/resolution/resolvelib/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,13 @@ def _report_single_requirement_conflict(

return DistributionNotFound(f"No matching distribution found for {req}")

def _has_any_candidates(self, project_name: str) -> bool:
"""
Check if there are any candidates available for the project name.
"""
candidates = self._finder.find_all_candidates(project_name)
return any(not c.link.is_yanked for c in candidates)

def get_installation_error(
self,
e: ResolutionImpossible[Requirement, Candidate],
Expand Down Expand Up @@ -796,6 +803,22 @@ def describe_trigger(parent: Candidate) -> str:
spec = constraints[key].specifier
msg += f"\n The user requested (constraint) {key}{spec}"

# Check for causes that had no candidates
causes = set()
for req, _ in e.causes:
causes.add(req.name)

no_candidates = {c for c in causes if not self._has_any_candidates(c)}
if no_candidates:
msg = (
msg
+ "\n\n"
+ "Some conflict cause(s) have no "
+ "available versions for your environment:"
+ "\n "
+ "\n ".join(sorted(no_candidates))
)

msg = (
msg
+ "\n\n"
Expand Down
Loading