7 from pip
._internal
.exceptions
import InvalidSchemeCombination
, UserInstallationInvalid
8 from pip
._internal
.models
.scheme
import SCHEME_KEYS
, Scheme
9 from pip
._internal
.utils
.virtualenv
import running_under_virtualenv
11 from .base
import change_root
, get_major_minor_version
, is_osx_framework
13 logger
= logging
.getLogger(__name__
)
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
24 _AVAILABLE_SCHEMES
= set(sysconfig
.get_scheme_names())
26 _PREFERRED_SCHEME_API
= getattr(sysconfig
, "get_preferred_scheme", None)
29 def _should_use_osx_framework_prefix() -> bool:
30 """Check for Apple's ``osx_framework_library`` scheme.
32 Python distributed by Apple's Command Line Tools has this special scheme
35 * This is a framework build.
36 * We are installing into the system prefix.
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.
49 "osx_framework_library" in _AVAILABLE_SCHEMES
50 and not running_under_virtualenv()
51 and is_osx_framework()
55 def _infer_prefix() -> str:
56 """Try to find a prefix scheme for the current platform.
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``).
67 If none of the above works, fall back to ``posix_prefix``.
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
:
81 if os
.name
in _AVAILABLE_SCHEMES
: # On Windows, prefx is just called "nt".
86 def _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"
93 suffixed
= f
"{os.name}_user"
94 if suffixed
in _AVAILABLE_SCHEMES
:
96 if "posix_user" not in _AVAILABLE_SCHEMES
: # User scheme unavailable.
97 raise UserInstallationInvalid()
101 def _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
:
111 # Update these keys if the user sets a custom home.
115 "installed_platbase",
120 if sysconfig
.get_config_var("userbase") is not None:
121 _HOME_KEYS
.append("userbase")
127 home
: typing
.Optional
[str] = None,
128 root
: typing
.Optional
[str] = None,
129 isolated
: bool = False,
130 prefix
: typing
.Optional
[str] = None,
133 Get the "scheme" corresponding to the input parameters.
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
146 raise InvalidSchemeCombination("--user", "--prefix")
148 raise InvalidSchemeCombination("--home", "--prefix")
151 scheme_name
= _infer_home()
153 scheme_name
= _infer_user()
155 scheme_name
= _infer_prefix()
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"
164 variables
= {k: home for k in _HOME_KEYS}
165 elif prefix
is not None:
166 variables
= {k: prefix for k in _HOME_KEYS}
170 paths
= sysconfig
.get_paths(scheme
=scheme_name
, vars=variables
)
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():
179 base
= variables
.get("userbase", sys
.prefix
)
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
)
185 dist_name
= "UNKNOWN"
188 platlib
=paths
["platlib"],
189 purelib
=paths
["purelib"],
190 headers
=os
.path
.join(paths
["include"], dist_name
),
191 scripts
=paths
["scripts"],
195 for key
in SCHEME_KEYS
:
196 value
= change_root(root
, getattr(scheme
, key
))
197 setattr(scheme
, key
, value
)
201 def 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"]
208 def get_purelib() -> str:
209 return sysconfig
.get_paths()["purelib"]
212 def get_platlib() -> str:
213 return sysconfig
.get_paths()["platlib"]