found_candidates.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. """Utilities to lazily create and visit candidates found.
  2. Creating and visiting a candidate is a *very* costly operation. It involves
  3. fetching, extracting, potentially building modules from source, and verifying
  4. distribution metadata. It is therefore crucial for performance to keep
  5. everything here lazy all the way down, so we only touch candidates that we
  6. absolutely need, and not "download the world" when we only need one version of
  7. something.
  8. """
  9. import itertools
  10. from pip._vendor.six.moves import collections_abc # type: ignore
  11. from pip._internal.utils.compat import lru_cache
  12. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  13. if MYPY_CHECK_RUNNING:
  14. from typing import Callable, Iterator, Optional
  15. from .base import Candidate
  16. def _insert_installed(installed, others):
  17. # type: (Candidate, Iterator[Candidate]) -> Iterator[Candidate]
  18. """Iterator for ``FoundCandidates``.
  19. This iterator is used when the resolver prefers to upgrade an
  20. already-installed package. Candidates from index are returned in their
  21. normal ordering, except replaced when the version is already installed.
  22. The implementation iterates through and yields other candidates, inserting
  23. the installed candidate exactly once before we start yielding older or
  24. equivalent candidates, or after all other candidates if they are all newer.
  25. """
  26. installed_yielded = False
  27. for candidate in others:
  28. # If the installed candidate is better, yield it first.
  29. if not installed_yielded and installed.version >= candidate.version:
  30. yield installed
  31. installed_yielded = True
  32. yield candidate
  33. # If the installed candidate is older than all other candidates.
  34. if not installed_yielded:
  35. yield installed
  36. class FoundCandidates(collections_abc.Sequence):
  37. """A lazy sequence to provide candidates to the resolver.
  38. The intended usage is to return this from `find_matches()` so the resolver
  39. can iterate through the sequence multiple times, but only access the index
  40. page when remote packages are actually needed. This improve performances
  41. when suitable candidates are already installed on disk.
  42. """
  43. def __init__(
  44. self,
  45. get_others, # type: Callable[[], Iterator[Candidate]]
  46. installed, # type: Optional[Candidate]
  47. prefers_installed, # type: bool
  48. ):
  49. self._get_others = get_others
  50. self._installed = installed
  51. self._prefers_installed = prefers_installed
  52. def __getitem__(self, index):
  53. # type: (int) -> Candidate
  54. # Implemented to satisfy the ABC check. This is not needed by the
  55. # resolver, and should not be used by the provider either (for
  56. # performance reasons).
  57. raise NotImplementedError("don't do this")
  58. def __iter__(self):
  59. # type: () -> Iterator[Candidate]
  60. if not self._installed:
  61. return self._get_others()
  62. others = (
  63. candidate
  64. for candidate in self._get_others()
  65. if candidate.version != self._installed.version
  66. )
  67. if self._prefers_installed:
  68. return itertools.chain([self._installed], others)
  69. return _insert_installed(self._installed, others)
  70. def __len__(self):
  71. # type: () -> int
  72. # Implemented to satisfy the ABC check. This is not needed by the
  73. # resolver, and should not be used by the provider either (for
  74. # performance reasons).
  75. raise NotImplementedError("don't do this")
  76. @lru_cache(maxsize=1)
  77. def __bool__(self):
  78. # type: () -> bool
  79. if self._prefers_installed and self._installed:
  80. return True
  81. return any(self)
  82. __nonzero__ = __bool__ # XXX: Python 2.