Skip to content

Commit 05cfa6f

Browse files
committed
Avoid candidates of the same version are created
Since 41a3008, Candidate objects prepare their underlying distribution eagerly on creation, so the error can be caught deterministically to trigger backtracking. This however has a negative side-effect. Since dist preparation involves metadata validation, a remote distribution must be downloaded on Candidate creation. This means that an sdist will be downloaded, validated, and discarded (since its version is known to be incompatible) during backtracking. This commit moves version deduplication of candidates from indexes to before the Candidate object is created, to avoid unneeded preparation. Note that we still need another round of deduplication in FoundCandidates to remove duplicated candidates when a distribution is already installed.
1 parent 47493d8 commit 05cfa6f

File tree

2 files changed

+14
-19
lines changed

2 files changed

+14
-19
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,13 @@ def iter_index_candidates():
228228
all_yanked = all(ican.link.is_yanked for ican in icans)
229229

230230
# PackageFinder returns earlier versions first, so we reverse.
231+
yielded = set() # type: Set[_BaseVersion]
231232
for ican in reversed(icans):
232233
if not all_yanked and ican.link.is_yanked:
233234
continue
235+
if ican.version in yielded:
236+
continue
237+
yielded.add(ican.version)
234238
candidate = self._make_candidate_from_link(
235239
link=ican.link,
236240
extras=extras,

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

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,11 @@
1616
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
1717

1818
if MYPY_CHECK_RUNNING:
19-
from typing import Callable, Iterator, Optional, Set
20-
21-
from pip._vendor.packaging.version import _BaseVersion
19+
from typing import Callable, Iterator, Optional
2220

2321
from .base import Candidate
2422

2523

26-
def _deduplicated_by_version(candidates):
27-
# type: (Iterator[Candidate]) -> Iterator[Candidate]
28-
returned = set() # type: Set[_BaseVersion]
29-
for candidate in candidates:
30-
if candidate.version in returned:
31-
continue
32-
returned.add(candidate.version)
33-
yield candidate
34-
35-
3624
def _insert_installed(installed, others):
3725
# type: (Candidate, Iterator[Candidate]) -> Iterator[Candidate]
3826
"""Iterator for ``FoundCandidates``.
@@ -86,12 +74,15 @@ def __getitem__(self, index):
8674
def __iter__(self):
8775
# type: () -> Iterator[Candidate]
8876
if not self._installed:
89-
candidates = self._get_others()
90-
elif self._prefers_installed:
91-
candidates = itertools.chain([self._installed], self._get_others())
92-
else:
93-
candidates = _insert_installed(self._installed, self._get_others())
94-
return _deduplicated_by_version(candidates)
77+
return self._get_others()
78+
others = (
79+
candidate
80+
for candidate in self._get_others()
81+
if candidate.version != self._installed.version
82+
)
83+
if self._prefers_installed:
84+
return itertools.chain([self._installed], others)
85+
return _insert_installed(self._installed, others)
9586

9687
def __len__(self):
9788
# type: () -> int

0 commit comments

Comments
 (0)