Skip to content

Commit 729c626

Browse files
uranusjrsbidoul
authored andcommitted
Exclude a known incompatible installed candidate
The resolver collects previously known incompatibilites and sends them to the provider. But previously the provider does not correctly exclude the currently-installed candidate if it is present in that incompatibility list, causing the resolver to enter a loop trying that same candidate. This patch correctly applies incompat_ids when producing an AlreadyInstalledCandidate and exclude it if its id() is in the set.
1 parent 1c31d33 commit 729c626

File tree

2 files changed

+27
-13
lines changed

2 files changed

+27
-13
lines changed

news/9841.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
New resolver: Correctly exclude an already installed package if its version is
2+
known to be incompatible to stop the dependency resolution process with a clear
3+
error message.

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,29 @@ def _iter_found_candidates(
240240
hashes &= ireq.hashes(trust_internet=False)
241241
extras |= frozenset(ireq.extras)
242242

243-
# Get the installed version, if it matches, unless the user
244-
# specified `--force-reinstall`, when we want the version from
245-
# the index instead.
246-
installed_candidate = None
247-
if not self._force_reinstall and name in self._installed_dists:
248-
installed_dist = self._installed_dists[name]
249-
if specifier.contains(installed_dist.version, prereleases=True):
250-
installed_candidate = self._make_candidate_from_dist(
251-
dist=installed_dist,
252-
extras=extras,
253-
template=template,
254-
)
243+
def _get_installed_candidate() -> Optional[Candidate]:
244+
"""Get the candidate for the currently-installed version."""
245+
# If --force-reinstall is set, we want the version from the index
246+
# instead, so we "pretend" there is nothing installed.
247+
if self._force_reinstall:
248+
return None
249+
try:
250+
installed_dist = self._installed_dists[name]
251+
except KeyError:
252+
return None
253+
# Don't use the installed distribution if its version does not fit
254+
# the current dependency graph.
255+
if not specifier.contains(installed_dist.version, prereleases=True):
256+
return None
257+
candidate = self._make_candidate_from_dist(
258+
dist=installed_dist,
259+
extras=extras,
260+
template=template,
261+
)
262+
# The candidate is a known incompatiblity. Don't use it.
263+
if id(candidate) in incompatible_ids:
264+
return None
265+
return candidate
255266

256267
def iter_index_candidate_infos():
257268
# type: () -> Iterator[IndexCandidateInfo]
@@ -283,7 +294,7 @@ def iter_index_candidate_infos():
283294

284295
return FoundCandidates(
285296
iter_index_candidate_infos,
286-
installed_candidate,
297+
_get_installed_candidate(),
287298
prefers_installed,
288299
incompatible_ids,
289300
)

0 commit comments

Comments
 (0)