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

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

93 statements  

1from __future__ import annotations 

2 

3import logging 

4import os 

5import sys 

6import sysconfig 

7from typing import Callable 

8 

9from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid 

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

11from pip._internal.utils.virtualenv import running_under_virtualenv 

12 

13from .base import change_root, get_major_minor_version, is_osx_framework 

14 

15logger = logging.getLogger(__name__) 

16 

17 

18# Notes on _infer_* functions. 

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

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

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

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

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

24# a POSIX scheme. 

25 

26_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names()) 

27 

28_PREFERRED_SCHEME_API: Callable[[str], str] | None = getattr( 

29 sysconfig, "get_preferred_scheme", None 

30) 

31 

32 

33def _should_use_osx_framework_prefix() -> bool: 

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

35 

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

37 that's used when: 

38 

39 * This is a framework build. 

40 * We are installing into the system prefix. 

41 

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

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

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

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

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

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

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

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

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

51 """ 

52 return ( 

53 "osx_framework_library" in _AVAILABLE_SCHEMES 

54 and not running_under_virtualenv() 

55 and is_osx_framework() 

56 ) 

57 

58 

59def _infer_prefix() -> str: 

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

61 

62 This tries: 

63 

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

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

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

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

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

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

70 

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

72 """ 

73 if _PREFERRED_SCHEME_API: 

74 return _PREFERRED_SCHEME_API("prefix") 

75 if _should_use_osx_framework_prefix(): 

76 return "osx_framework_library" 

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

78 if implementation_suffixed in _AVAILABLE_SCHEMES: 

79 return implementation_suffixed 

80 if sys.implementation.name in _AVAILABLE_SCHEMES: 

81 return sys.implementation.name 

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

83 if suffixed in _AVAILABLE_SCHEMES: 

84 return suffixed 

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

86 return os.name 

87 return "posix_prefix" 

88 

89 

90def _infer_user() -> str: 

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

92 if _PREFERRED_SCHEME_API: 

93 return _PREFERRED_SCHEME_API("user") 

94 if is_osx_framework() and not running_under_virtualenv(): 

95 suffixed = "osx_framework_user" 

96 else: 

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

98 if suffixed in _AVAILABLE_SCHEMES: 

99 return suffixed 

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

101 raise UserInstallationInvalid() 

102 return "posix_user" 

103 

104 

105def _infer_home() -> str: 

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

107 if _PREFERRED_SCHEME_API: 

108 return _PREFERRED_SCHEME_API("home") 

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

110 if suffixed in _AVAILABLE_SCHEMES: 

111 return suffixed 

112 return "posix_home" 

113 

114 

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

116_HOME_KEYS = [ 

117 "installed_base", 

118 "base", 

119 "installed_platbase", 

120 "platbase", 

121 "prefix", 

122 "exec_prefix", 

123] 

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

125 _HOME_KEYS.append("userbase") 

126 

127 

128def get_scheme( 

129 dist_name: str, 

130 user: bool = False, 

131 home: str | None = None, 

132 root: str | None = None, 

133 isolated: bool = False, 

134 prefix: str | None = None, 

135) -> Scheme: 

136 """ 

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

138 

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

140 in the headers scheme path 

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

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

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

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

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

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

147 base directory for the same 

148 """ 

149 if user and prefix: 

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

151 if home and prefix: 

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

153 

154 if home is not None: 

155 scheme_name = _infer_home() 

156 elif user: 

157 scheme_name = _infer_user() 

158 else: 

159 scheme_name = _infer_prefix() 

160 

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

162 # instead of osx_framework_library. See _should_use_osx_framework_prefix() 

163 # docstring for details. 

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

165 scheme_name = "posix_prefix" 

166 

167 if home is not None: 

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

169 elif prefix is not None: 

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

171 else: 

172 variables = {} 

173 

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

175 

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

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

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

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

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

181 if running_under_virtualenv(): 

182 if user: 

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

184 else: 

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

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

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

188 elif not dist_name: 

189 dist_name = "UNKNOWN" 

190 

191 scheme = Scheme( 

192 platlib=paths["platlib"], 

193 purelib=paths["purelib"], 

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

195 scripts=paths["scripts"], 

196 data=paths["data"], 

197 ) 

198 if root is not None: 

199 converted_keys = {} 

200 for key in SCHEME_KEYS: 

201 converted_keys[key] = change_root(root, getattr(scheme, key)) 

202 scheme = Scheme(**converted_keys) 

203 return scheme 

204 

205 

206def get_bin_prefix() -> str: 

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

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

209 return "/usr/local/bin" 

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

211 

212 

213def get_purelib() -> str: 

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

215 

216 

217def get_platlib() -> str: 

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