|
import contextlib |
|
import functools |
|
import os |
|
import sys |
|
from typing import TYPE_CHECKING, List, Optional, Type, cast |
|
|
|
from pip._internal.utils.misc import strtobool |
|
|
|
from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel |
|
|
|
if TYPE_CHECKING: |
|
from typing import Protocol |
|
else: |
|
Protocol = object |
|
|
|
__all__ = [ |
|
"BaseDistribution", |
|
"BaseEnvironment", |
|
"FilesystemWheel", |
|
"MemoryWheel", |
|
"Wheel", |
|
"get_default_environment", |
|
"get_environment", |
|
"get_wheel_distribution", |
|
"select_backend", |
|
] |
|
|
|
|
|
def _should_use_importlib_metadata() -> bool: |
|
"""Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend. |
|
|
|
By default, pip uses ``importlib.metadata`` on Python 3.11+, and |
|
``pkg_resourcess`` otherwise. This can be overridden by a couple of ways: |
|
|
|
* If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it |
|
dictates whether ``importlib.metadata`` is used, regardless of Python |
|
version. |
|
* On Python 3.11+, Python distributors can patch ``importlib.metadata`` |
|
to add a global constant ``_PIP_USE_IMPORTLIB_METADATA = False``. This |
|
makes pip use ``pkg_resources`` (unless the user set the aforementioned |
|
environment variable to *True*). |
|
""" |
|
with contextlib.suppress(KeyError, ValueError): |
|
return bool(strtobool(os.environ["_PIP_USE_IMPORTLIB_METADATA"])) |
|
if sys.version_info < (3, 11): |
|
return False |
|
import importlib.metadata |
|
|
|
return bool(getattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA", True)) |
|
|
|
|
|
class Backend(Protocol): |
|
Distribution: Type[BaseDistribution] |
|
Environment: Type[BaseEnvironment] |
|
|
|
|
|
@functools.lru_cache(maxsize=None) |
|
def select_backend() -> Backend: |
|
if _should_use_importlib_metadata(): |
|
from . import importlib |
|
|
|
return cast(Backend, importlib) |
|
from . import pkg_resources |
|
|
|
return cast(Backend, pkg_resources) |
|
|
|
|
|
def get_default_environment() -> BaseEnvironment: |
|
"""Get the default representation for the current environment. |
|
|
|
This returns an Environment instance from the chosen backend. The default |
|
Environment instance should be built from ``sys.path`` and may use caching |
|
to share instance state accorss calls. |
|
""" |
|
return select_backend().Environment.default() |
|
|
|
|
|
def get_environment(paths: Optional[List[str]]) -> BaseEnvironment: |
|
"""Get a representation of the environment specified by ``paths``. |
|
|
|
This returns an Environment instance from the chosen backend based on the |
|
given import paths. The backend must build a fresh instance representing |
|
the state of installed distributions when this function is called. |
|
""" |
|
return select_backend().Environment.from_paths(paths) |
|
|
|
|
|
def get_directory_distribution(directory: str) -> BaseDistribution: |
|
"""Get the distribution metadata representation in the specified directory. |
|
|
|
This returns a Distribution instance from the chosen backend based on |
|
the given on-disk ``.dist-info`` directory. |
|
""" |
|
return select_backend().Distribution.from_directory(directory) |
|
|
|
|
|
def get_wheel_distribution(wheel: Wheel, canonical_name: str) -> BaseDistribution: |
|
"""Get the representation of the specified wheel's distribution metadata. |
|
|
|
This returns a Distribution instance from the chosen backend based on |
|
the given wheel's ``.dist-info`` directory. |
|
|
|
:param canonical_name: Normalized project name of the given wheel. |
|
""" |
|
return select_backend().Distribution.from_wheel(wheel, canonical_name) |
|
|
|
|
|
def get_metadata_distribution( |
|
metadata_contents: bytes, |
|
filename: str, |
|
canonical_name: str, |
|
) -> BaseDistribution: |
|
"""Get the dist representation of the specified METADATA file contents. |
|
|
|
This returns a Distribution instance from the chosen backend sourced from the data |
|
in `metadata_contents`. |
|
|
|
:param metadata_contents: Contents of a METADATA file within a dist, or one served |
|
via PEP 658. |
|
:param filename: Filename for the dist this metadata represents. |
|
:param canonical_name: Normalized project name of the given dist. |
|
""" |
|
return select_backend().Distribution.from_metadata_file_contents( |
|
metadata_contents, |
|
filename, |
|
canonical_name, |
|
) |
|
|