Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pip/_internal/locations/_sysconfig.py: 34%

91 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:48 +0000

1import logging 

2import os 

3import sys 

4import sysconfig 

5import typing 

6 

7from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid 

8from pip._internal.models.scheme import SCHEME_KEYS, Scheme 

9from pip._internal.utils.virtualenv import running_under_virtualenv 

10 

11from .base import change_root, get_major_minor_version, is_osx_framework 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16# Notes on _infer_* functions. 

17# Unfortunately ``get_default_scheme()`` didn't exist before 3.10, so there's no 

18# way to ask things like "what is the '_prefix' scheme on this platform". These 

19# functions try to answer that with some heuristics while accounting for ad-hoc 

20# platforms not covered by CPython's default sysconfig implementation. If the 

21# ad-hoc implementation does not fully implement sysconfig, we'll fall back to 

22# a POSIX scheme. 

23 

24_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names()) 

25 

26_PREFERRED_SCHEME_API = getattr(sysconfig, "get_preferred_scheme", None) 

27 

28 

29def _should_use_osx_framework_prefix() -> bool: 

30 """Check for Apple's ``osx_framework_library`` scheme. 

31 

32 Python distributed by Apple's Command Line Tools has this special scheme 

33 that's used when: 

34 

35 * This is a framework build. 

36 * We are installing into the system prefix. 

37 

38 This does not account for ``pip install --prefix`` (also means we're not 

39 installing to the system prefix), which should use ``posix_prefix``, but 

40 logic here means ``_infer_prefix()`` outputs ``osx_framework_library``. But 

41 since ``prefix`` is not available for ``sysconfig.get_default_scheme()``, 

42 which is the stdlib replacement for ``_infer_prefix()``, presumably Apple 

43 wouldn't be able to magically switch between ``osx_framework_library`` and 

44 ``posix_prefix``. ``_infer_prefix()`` returning ``osx_framework_library`` 

45 means its behavior is consistent whether we use the stdlib implementation 

46 or our own, and we deal with this special case in ``get_scheme()`` instead. 

47 """ 

48 return ( 

49 "osx_framework_library" in _AVAILABLE_SCHEMES 

50 and not running_under_virtualenv() 

51 and is_osx_framework() 

52 ) 

53 

54 

55def _infer_prefix() -> str: 

56 """Try to find a prefix scheme for the current platform. 

57 

58 This tries: 

59 

60 * A special ``osx_framework_library`` for Python distributed by Apple's 

61 Command Line Tools, when not running in a virtual environment. 

62 * Implementation + OS, used by PyPy on Windows (``pypy_nt``). 

63 * Implementation without OS, used by PyPy on POSIX (``pypy``). 

64 * OS + "prefix", used by CPython on POSIX (``posix_prefix``). 

65 * Just the OS name, used by CPython on Windows (``nt``). 

66 

67 If none of the above works, fall back to ``posix_prefix``. 

68 """ 

69 if _PREFERRED_SCHEME_API: 

70 return _PREFERRED_SCHEME_API("prefix") 

71 if _should_use_osx_framework_prefix(): 

72 return "osx_framework_library" 

73 implementation_suffixed = f"{sys.implementation.name}_{os.name}" 

74 if implementation_suffixed in _AVAILABLE_SCHEMES: 

75 return implementation_suffixed 

76 if sys.implementation.name in _AVAILABLE_SCHEMES: 

77 return sys.implementation.name 

78 suffixed = f"{os.name}_prefix" 

79 if suffixed in _AVAILABLE_SCHEMES: 

80 return suffixed 

81 if os.name in _AVAILABLE_SCHEMES: # On Windows, prefx is just called "nt". 

82 return os.name 

83 return "posix_prefix" 

84 

85 

86def _infer_user() -> str: 

87 """Try to find a user scheme for the current platform.""" 

88 if _PREFERRED_SCHEME_API: 

89 return _PREFERRED_SCHEME_API("user") 

90 if is_osx_framework() and not running_under_virtualenv(): 

91 suffixed = "osx_framework_user" 

92 else: 

93 suffixed = f"{os.name}_user" 

94 if suffixed in _AVAILABLE_SCHEMES: 

95 return suffixed 

96 if "posix_user" not in _AVAILABLE_SCHEMES: # User scheme unavailable. 

97 raise UserInstallationInvalid() 

98 return "posix_user" 

99 

100 

101def _infer_home() -> str: 

102 """Try to find a home for the current platform.""" 

103 if _PREFERRED_SCHEME_API: 

104 return _PREFERRED_SCHEME_API("home") 

105 suffixed = f"{os.name}_home" 

106 if suffixed in _AVAILABLE_SCHEMES: 

107 return suffixed 

108 return "posix_home" 

109 

110 

111# Update these keys if the user sets a custom home. 

112_HOME_KEYS = [ 

113 "installed_base", 

114 "base", 

115 "installed_platbase", 

116 "platbase", 

117 "prefix", 

118 "exec_prefix", 

119] 

120if sysconfig.get_config_var("userbase") is not None: 

121 _HOME_KEYS.append("userbase") 

122 

123 

124def get_scheme( 

125 dist_name: str, 

126 user: bool = False, 

127 home: typing.Optional[str] = None, 

128 root: typing.Optional[str] = None, 

129 isolated: bool = False, 

130 prefix: typing.Optional[str] = None, 

131) -> Scheme: 

132 """ 

133 Get the "scheme" corresponding to the input parameters. 

134 

135 :param dist_name: the name of the package to retrieve the scheme for, used 

136 in the headers scheme path 

137 :param user: indicates to use the "user" scheme 

138 :param home: indicates to use the "home" scheme 

139 :param root: root under which other directories are re-based 

140 :param isolated: ignored, but kept for distutils compatibility (where 

141 this controls whether the user-site pydistutils.cfg is honored) 

142 :param prefix: indicates to use the "prefix" scheme and provides the 

143 base directory for the same 

144 """ 

145 if user and prefix: 

146 raise InvalidSchemeCombination("--user", "--prefix") 

147 if home and prefix: 

148 raise InvalidSchemeCombination("--home", "--prefix") 

149 

150 if home is not None: 

151 scheme_name = _infer_home() 

152 elif user: 

153 scheme_name = _infer_user() 

154 else: 

155 scheme_name = _infer_prefix() 

156 

157 # Special case: When installing into a custom prefix, use posix_prefix 

158 # instead of osx_framework_library. See _should_use_osx_framework_prefix() 

159 # docstring for details. 

160 if prefix is not None and scheme_name == "osx_framework_library": 

161 scheme_name = "posix_prefix" 

162 

163 if home is not None: 

164 variables = {k: home for k in _HOME_KEYS} 

165 elif prefix is not None: 

166 variables = {k: prefix for k in _HOME_KEYS} 

167 else: 

168 variables = {} 

169 

170 paths = sysconfig.get_paths(scheme=scheme_name, vars=variables) 

171 

172 # Logic here is very arbitrary, we're doing it for compatibility, don't ask. 

173 # 1. Pip historically uses a special header path in virtual environments. 

174 # 2. If the distribution name is not known, distutils uses 'UNKNOWN'. We 

175 # only do the same when not running in a virtual environment because 

176 # pip's historical header path logic (see point 1) did not do this. 

177 if running_under_virtualenv(): 

178 if user: 

179 base = variables.get("userbase", sys.prefix) 

180 else: 

181 base = variables.get("base", sys.prefix) 

182 python_xy = f"python{get_major_minor_version()}" 

183 paths["include"] = os.path.join(base, "include", "site", python_xy) 

184 elif not dist_name: 

185 dist_name = "UNKNOWN" 

186 

187 scheme = Scheme( 

188 platlib=paths["platlib"], 

189 purelib=paths["purelib"], 

190 headers=os.path.join(paths["include"], dist_name), 

191 scripts=paths["scripts"], 

192 data=paths["data"], 

193 ) 

194 if root is not None: 

195 for key in SCHEME_KEYS: 

196 value = change_root(root, getattr(scheme, key)) 

197 setattr(scheme, key, value) 

198 return scheme 

199 

200 

201def get_bin_prefix() -> str: 

202 # Forcing to use /usr/local/bin for standard macOS framework installs. 

203 if sys.platform[:6] == "darwin" and sys.prefix[:16] == "/System/Library/": 

204 return "/usr/local/bin" 

205 return sysconfig.get_paths()["scripts"] 

206 

207 

208def get_purelib() -> str: 

209 return sysconfig.get_paths()["purelib"] 

210 

211 

212def get_platlib() -> str: 

213 return sysconfig.get_paths()["platlib"]