Do this all over again

This commit is contained in:
Tzu-ping Chung 2020-10-01 23:41:03 +08:00
parent d22775819b
commit 17d0086ea2
1 changed files with 38 additions and 71 deletions

View File

@ -1,89 +1,51 @@
import functools
import itertools
from pip._vendor.six.moves import collections_abc # type: ignore from pip._vendor.six.moves import collections_abc # type: ignore
from pip._internal.utils.compat import lru_cache from pip._internal.utils.compat import lru_cache
from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING: if MYPY_CHECK_RUNNING:
from typing import Callable, Iterator, Optional, Set from typing import Any, Callable, Iterator, Optional, Set
from pip._vendor.packaging.version import _BaseVersion from pip._vendor.packaging.version import _BaseVersion
from .base import Candidate from .base import Candidate
class _InstalledFirstCandidatesIterator(collections_abc.Iterator): def _deduplicated_by_version(candidates):
"""Iterator for ``FoundCandidates``. # type: (Iterator[Candidate]) -> Iterator[Candidate]
returned = set() # type: Set[_BaseVersion]
This iterator is used when the resolver prefers to keep the version of an for candidate in candidates:
already-installed package. The already-installed candidate is always if candidate.version in returned:
returned first. Candidates from index are accessed only when the resolver continue
wants them, and the already-installed version is excluded from them. returned.add(candidate.version)
""" yield candidate
def __init__(
self,
get_others, # type: Callable[[], Iterator[Candidate]]
installed, # type: Optional[Candidate]
):
self._installed = installed
self._get_others = get_others
self._others = None # type: Optional[Iterator[Candidate]]
self._returned = set() # type: Set[_BaseVersion]
def __next__(self):
# type: () -> Candidate
if self._installed and self._installed.version not in self._returned:
self._returned.add(self._installed.version)
return self._installed
if self._others is None:
self._others = self._get_others()
cand = next(self._others)
while cand.version in self._returned:
cand = next(self._others)
self._returned.add(cand.version)
return cand
next = __next__ # XXX: Python 2.
class _InstalledReplacesCandidatesIterator(collections_abc.Iterator): def _replaces_sort_key(installed, candidate):
# type: (Candidate, Candidate) -> Any
return (candidate.version, candidate is installed)
def _insert_installed(installed, others):
# type: (Candidate, Iterator[Candidate]) -> Iterator[Candidate]
"""Iterator for ``FoundCandidates``. """Iterator for ``FoundCandidates``.
This iterator is used when the resolver prefers to upgrade an This iterator is used when the resolver prefers to upgrade an
already-installed package. Candidates from index are returned in their already-installed package. Candidates from index are returned in their
normal ordering, except replaced when the version is already installed. normal ordering, except replaced when the version is already installed.
The sort key prefers the installed candidate over candidates of the same
version from the index, so it is chosen on de-duplication.
""" """
def __init__( candidates = sorted(
self, itertools.chain(others, [installed]),
get_others, # type: Callable[[], Iterator[Candidate]] key=functools.partial(_replaces_sort_key, installed),
installed, # type: Optional[Candidate] reverse=True,
): )
self._installed = installed return iter(candidates)
self._get_others = get_others
self._others = None # type: Optional[Iterator[Candidate]]
self._returned = set() # type: Set[_BaseVersion]
def __next__(self):
# type: () -> Candidate
if self._others is None:
self._others = self._get_others()
try:
cand = next(self._others)
while cand.version in self._returned:
cand = next(self._others)
if self._installed and cand.version == self._installed.version:
cand = self._installed
except StopIteration:
# Return the already-installed candidate as the last item if its
# version does not exist on the index.
if not self._installed:
raise
if self._installed.version in self._returned:
raise
cand = self._installed
self._returned.add(cand.version)
return cand
next = __next__ # XXX: Python 2.
class FoundCandidates(collections_abc.Sequence): class FoundCandidates(collections_abc.Sequence):
@ -106,22 +68,27 @@ class FoundCandidates(collections_abc.Sequence):
def __getitem__(self, index): def __getitem__(self, index):
# type: (int) -> Candidate # type: (int) -> Candidate
# Implemented to satisfy the ABC check, This is not needed by the # Implemented to satisfy the ABC check. This is not needed by the
# resolver, and should not be used by the provider either (for # resolver, and should not be used by the provider either (for
# performance reasons). # performance reasons).
raise NotImplementedError("don't do this") raise NotImplementedError("don't do this")
def __iter__(self): def __iter__(self):
# type: () -> Iterator[Candidate] # type: () -> Iterator[Candidate]
if self._prefers_installed: if not self._installed:
klass = _InstalledFirstCandidatesIterator candidates = self._get_others()
elif self._prefers_installed:
candidates = itertools.chain([self._installed], self._get_others())
else: else:
klass = _InstalledReplacesCandidatesIterator candidates = _insert_installed(self._installed, self._get_others())
return klass(self._get_others, self._installed) return _deduplicated_by_version(candidates)
@lru_cache(maxsize=1) @lru_cache(maxsize=1)
def __len__(self): def __len__(self):
# type: () -> int # type: () -> int
# Implement to satisfy the ABC check and used in tests. This is not
# needed by the resolver, and should not be used by the provider either
# (for performance reasons).
return sum(1 for _ in self) return sum(1 for _ in self)
@lru_cache(maxsize=1) @lru_cache(maxsize=1)