__init__.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import contextlib
  2. import functools
  3. import os
  4. import sys
  5. from typing import TYPE_CHECKING, List, Optional, Type, cast
  6. from pip._internal.utils.misc import strtobool
  7. from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel
  8. if TYPE_CHECKING:
  9. from typing import Protocol
  10. else:
  11. Protocol = object
  12. __all__ = [
  13. "BaseDistribution",
  14. "BaseEnvironment",
  15. "FilesystemWheel",
  16. "MemoryWheel",
  17. "Wheel",
  18. "get_default_environment",
  19. "get_environment",
  20. "get_wheel_distribution",
  21. "select_backend",
  22. ]
  23. def _should_use_importlib_metadata() -> bool:
  24. """Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend.
  25. By default, pip uses ``importlib.metadata`` on Python 3.11+, and
  26. ``pkg_resourcess`` otherwise. This can be overridden by a couple of ways:
  27. * If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it
  28. dictates whether ``importlib.metadata`` is used, regardless of Python
  29. version.
  30. * On Python 3.11+, Python distributors can patch ``importlib.metadata``
  31. to add a global constant ``_PIP_USE_IMPORTLIB_METADATA = False``. This
  32. makes pip use ``pkg_resources`` (unless the user set the aforementioned
  33. environment variable to *True*).
  34. """
  35. with contextlib.suppress(KeyError, ValueError):
  36. return bool(strtobool(os.environ["_PIP_USE_IMPORTLIB_METADATA"]))
  37. if sys.version_info < (3, 11):
  38. return False
  39. import importlib.metadata
  40. return bool(getattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA", True))
  41. class Backend(Protocol):
  42. Distribution: Type[BaseDistribution]
  43. Environment: Type[BaseEnvironment]
  44. @functools.lru_cache(maxsize=None)
  45. def select_backend() -> Backend:
  46. if _should_use_importlib_metadata():
  47. from . import importlib
  48. return cast(Backend, importlib)
  49. from . import pkg_resources
  50. return cast(Backend, pkg_resources)
  51. def get_default_environment() -> BaseEnvironment:
  52. """Get the default representation for the current environment.
  53. This returns an Environment instance from the chosen backend. The default
  54. Environment instance should be built from ``sys.path`` and may use caching
  55. to share instance state accorss calls.
  56. """
  57. return select_backend().Environment.default()
  58. def get_environment(paths: Optional[List[str]]) -> BaseEnvironment:
  59. """Get a representation of the environment specified by ``paths``.
  60. This returns an Environment instance from the chosen backend based on the
  61. given import paths. The backend must build a fresh instance representing
  62. the state of installed distributions when this function is called.
  63. """
  64. return select_backend().Environment.from_paths(paths)
  65. def get_directory_distribution(directory: str) -> BaseDistribution:
  66. """Get the distribution metadata representation in the specified directory.
  67. This returns a Distribution instance from the chosen backend based on
  68. the given on-disk ``.dist-info`` directory.
  69. """
  70. return select_backend().Distribution.from_directory(directory)
  71. def get_wheel_distribution(wheel: Wheel, canonical_name: str) -> BaseDistribution:
  72. """Get the representation of the specified wheel's distribution metadata.
  73. This returns a Distribution instance from the chosen backend based on
  74. the given wheel's ``.dist-info`` directory.
  75. :param canonical_name: Normalized project name of the given wheel.
  76. """
  77. return select_backend().Distribution.from_wheel(wheel, canonical_name)
  78. def get_metadata_distribution(
  79. metadata_contents: bytes,
  80. filename: str,
  81. canonical_name: str,
  82. ) -> BaseDistribution:
  83. """Get the dist representation of the specified METADATA file contents.
  84. This returns a Distribution instance from the chosen backend sourced from the data
  85. in `metadata_contents`.
  86. :param metadata_contents: Contents of a METADATA file within a dist, or one served
  87. via PEP 658.
  88. :param filename: Filename for the dist this metadata represents.
  89. :param canonical_name: Normalized project name of the given dist.
  90. """
  91. return select_backend().Distribution.from_metadata_file_contents(
  92. metadata_contents,
  93. filename,
  94. canonical_name,
  95. )