Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/metadata/__init__.py: 68%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

50 statements  

1from __future__ import annotations 

2 

3import contextlib 

4import functools 

5import os 

6import sys 

7from typing import TYPE_CHECKING, Literal, Protocol, cast 

8 

9from pip._internal.utils.deprecation import deprecated 

10from pip._internal.utils.misc import strtobool 

11 

12from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel 

13 

14if TYPE_CHECKING: 

15 from pip._vendor.packaging.utils import NormalizedName 

16 

17__all__ = [ 

18 "BaseDistribution", 

19 "BaseEnvironment", 

20 "FilesystemWheel", 

21 "MemoryWheel", 

22 "Wheel", 

23 "get_default_environment", 

24 "get_environment", 

25 "get_wheel_distribution", 

26 "select_backend", 

27] 

28 

29 

30def _should_use_importlib_metadata() -> bool: 

31 """Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend. 

32 

33 By default, pip uses ``importlib.metadata`` on Python 3.11+, and 

34 ``pkg_resources`` otherwise. Up to Python 3.13, This can be 

35 overridden by a couple of ways: 

36 

37 * If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it 

38 dictates whether ``importlib.metadata`` is used, for Python <3.14. 

39 * On Python 3.11, 3.12 and 3.13, Python distributors can patch 

40 ``importlib.metadata`` to add a global constant 

41 ``_PIP_USE_IMPORTLIB_METADATA = False``. This makes pip use 

42 ``pkg_resources`` (unless the user set the aforementioned environment 

43 variable to *True*). 

44 

45 On Python 3.14+, the ``pkg_resources`` backend cannot be used. 

46 """ 

47 if sys.version_info >= (3, 14): 

48 # On Python >=3.14 we only support importlib.metadata. 

49 return True 

50 with contextlib.suppress(KeyError, ValueError): 

51 # On Python <3.14, if the environment variable is set, we obey what it says. 

52 return bool(strtobool(os.environ["_PIP_USE_IMPORTLIB_METADATA"])) 

53 if sys.version_info < (3, 11): 

54 # On Python <3.11, we always use pkg_resources, unless the environment 

55 # variable was set. 

56 return False 

57 # On Python 3.11, 3.12 and 3.13, we check if the global constant is set. 

58 import importlib.metadata 

59 

60 return bool(getattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA", True)) 

61 

62 

63def _emit_pkg_resources_deprecation_if_needed() -> None: 

64 if sys.version_info < (3, 11): 

65 # All pip versions supporting Python<=3.11 will support pkg_resources, 

66 # and pkg_resources is the default for these, so let's not bother users. 

67 return 

68 

69 import importlib.metadata 

70 

71 if hasattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA"): 

72 # The Python distributor has set the global constant, so we don't 

73 # warn, since it is not a user decision. 

74 return 

75 

76 # The user has decided to use pkg_resources, so we warn. 

77 deprecated( 

78 reason="Using the pkg_resources metadata backend is deprecated.", 

79 replacement=( 

80 "to use the default importlib.metadata backend, " 

81 "by unsetting the _PIP_USE_IMPORTLIB_METADATA environment variable" 

82 ), 

83 gone_in="26.3", 

84 issue=13317, 

85 ) 

86 

87 

88class Backend(Protocol): 

89 NAME: Literal["importlib", "pkg_resources"] 

90 Distribution: type[BaseDistribution] 

91 Environment: type[BaseEnvironment] 

92 

93 

94@functools.cache 

95def select_backend() -> Backend: 

96 if _should_use_importlib_metadata(): 

97 from . import importlib 

98 

99 return cast(Backend, importlib) 

100 

101 _emit_pkg_resources_deprecation_if_needed() 

102 

103 from . import pkg_resources 

104 

105 return cast(Backend, pkg_resources) 

106 

107 

108def get_default_environment() -> BaseEnvironment: 

109 """Get the default representation for the current environment. 

110 

111 This returns an Environment instance from the chosen backend. The default 

112 Environment instance should be built from ``sys.path`` and may use caching 

113 to share instance state across calls. 

114 """ 

115 return select_backend().Environment.default() 

116 

117 

118def get_environment(paths: list[str] | None) -> BaseEnvironment: 

119 """Get a representation of the environment specified by ``paths``. 

120 

121 This returns an Environment instance from the chosen backend based on the 

122 given import paths. The backend must build a fresh instance representing 

123 the state of installed distributions when this function is called. 

124 """ 

125 return select_backend().Environment.from_paths(paths) 

126 

127 

128def get_directory_distribution(directory: str) -> BaseDistribution: 

129 """Get the distribution metadata representation in the specified directory. 

130 

131 This returns a Distribution instance from the chosen backend based on 

132 the given on-disk ``.dist-info`` directory. 

133 """ 

134 return select_backend().Distribution.from_directory(directory) 

135 

136 

137def get_wheel_distribution( 

138 wheel: Wheel, canonical_name: NormalizedName 

139) -> BaseDistribution: 

140 """Get the representation of the specified wheel's distribution metadata. 

141 

142 This returns a Distribution instance from the chosen backend based on 

143 the given wheel's ``.dist-info`` directory. 

144 

145 :param canonical_name: Normalized project name of the given wheel. 

146 """ 

147 return select_backend().Distribution.from_wheel(wheel, canonical_name) 

148 

149 

150def get_metadata_distribution( 

151 metadata_contents: bytes, 

152 filename: str, 

153 canonical_name: str, 

154) -> BaseDistribution: 

155 """Get the dist representation of the specified METADATA file contents. 

156 

157 This returns a Distribution instance from the chosen backend sourced from the data 

158 in `metadata_contents`. 

159 

160 :param metadata_contents: Contents of a METADATA file within a dist, or one served 

161 via PEP 658. 

162 :param filename: Filename for the dist this metadata represents. 

163 :param canonical_name: Normalized project name of the given dist. 

164 """ 

165 return select_backend().Distribution.from_metadata_file_contents( 

166 metadata_contents, 

167 filename, 

168 canonical_name, 

169 )