| Viewing file:  _sysconfig.py (7.73 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
import distutils.util  # FIXME: For change_root.import logging
 import os
 import sys
 import sysconfig
 import typing
 
 from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid
 from pip._internal.models.scheme import SCHEME_KEYS, Scheme
 from pip._internal.utils.virtualenv import running_under_virtualenv
 
 from .base import get_major_minor_version, is_osx_framework
 
 logger = logging.getLogger(__name__)
 
 
 # Notes on _infer_* functions.
 # Unfortunately ``get_default_scheme()`` didn't exist before 3.10, so there's no
 # way to ask things like "what is the '_prefix' scheme on this platform". These
 # functions try to answer that with some heuristics while accounting for ad-hoc
 # platforms not covered by CPython's default sysconfig implementation. If the
 # ad-hoc implementation does not fully implement sysconfig, we'll fall back to
 # a POSIX scheme.
 
 _AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names())
 
 _PREFERRED_SCHEME_API = getattr(sysconfig, "get_preferred_scheme", None)
 
 
 def _should_use_osx_framework_prefix() -> bool:
 """Check for Apple's ``osx_framework_library`` scheme.
 
 Python distributed by Apple's Command Line Tools has this special scheme
 that's used when:
 
 * This is a framework build.
 * We are installing into the system prefix.
 
 This does not account for ``pip install --prefix`` (also means we're not
 installing to the system prefix), which should use ``posix_prefix``, but
 logic here means ``_infer_prefix()`` outputs ``osx_framework_library``. But
 since ``prefix`` is not available for ``sysconfig.get_default_scheme()``,
 which is the stdlib replacement for ``_infer_prefix()``, presumably Apple
 wouldn't be able to magically switch between ``osx_framework_library`` and
 ``posix_prefix``. ``_infer_prefix()`` returning ``osx_framework_library``
 means its behavior is consistent whether we use the stdlib implementation
 or our own, and we deal with this special case in ``get_scheme()`` instead.
 """
 return (
 "osx_framework_library" in _AVAILABLE_SCHEMES
 and not running_under_virtualenv()
 and is_osx_framework()
 )
 
 
 def _infer_prefix() -> str:
 """Try to find a prefix scheme for the current platform.
 
 This tries:
 
 * A special ``osx_framework_library`` for Python distributed by Apple's
 Command Line Tools, when not running in a virtual environment.
 * Implementation + OS, used by PyPy on Windows (``pypy_nt``).
 * Implementation without OS, used by PyPy on POSIX (``pypy``).
 * OS + "prefix", used by CPython on POSIX (``posix_prefix``).
 * Just the OS name, used by CPython on Windows (``nt``).
 
 If none of the above works, fall back to ``posix_prefix``.
 """
 if _PREFERRED_SCHEME_API:
 return _PREFERRED_SCHEME_API("prefix")
 if _should_use_osx_framework_prefix():
 return "osx_framework_library"
 implementation_suffixed = f"{sys.implementation.name}_{os.name}"
 if implementation_suffixed in _AVAILABLE_SCHEMES:
 return implementation_suffixed
 if sys.implementation.name in _AVAILABLE_SCHEMES:
 return sys.implementation.name
 suffixed = f"{os.name}_prefix"
 if suffixed in _AVAILABLE_SCHEMES:
 return suffixed
 if os.name in _AVAILABLE_SCHEMES:  # On Windows, prefx is just called "nt".
 return os.name
 return "posix_prefix"
 
 
 def _infer_user() -> str:
 """Try to find a user scheme for the current platform."""
 if _PREFERRED_SCHEME_API:
 return _PREFERRED_SCHEME_API("user")
 if is_osx_framework() and not running_under_virtualenv():
 suffixed = "osx_framework_user"
 else:
 suffixed = f"{os.name}_user"
 if suffixed in _AVAILABLE_SCHEMES:
 return suffixed
 if "posix_user" not in _AVAILABLE_SCHEMES:  # User scheme unavailable.
 raise UserInstallationInvalid()
 return "posix_user"
 
 
 def _infer_home() -> str:
 """Try to find a home for the current platform."""
 if _PREFERRED_SCHEME_API:
 return _PREFERRED_SCHEME_API("home")
 suffixed = f"{os.name}_home"
 if suffixed in _AVAILABLE_SCHEMES:
 return suffixed
 return "posix_home"
 
 
 # Update these keys if the user sets a custom home.
 _HOME_KEYS = [
 "installed_base",
 "base",
 "installed_platbase",
 "platbase",
 "prefix",
 "exec_prefix",
 ]
 if sysconfig.get_config_var("userbase") is not None:
 _HOME_KEYS.append("userbase")
 
 
 def get_scheme(
 dist_name: str,
 user: bool = False,
 home: typing.Optional[str] = None,
 root: typing.Optional[str] = None,
 isolated: bool = False,
 prefix: typing.Optional[str] = None,
 ) -> Scheme:
 """
 Get the "scheme" corresponding to the input parameters.
 
 :param dist_name: the name of the package to retrieve the scheme for, used
 in the headers scheme path
 :param user: indicates to use the "user" scheme
 :param home: indicates to use the "home" scheme
 :param root: root under which other directories are re-based
 :param isolated: ignored, but kept for distutils compatibility (where
 this controls whether the user-site pydistutils.cfg is honored)
 :param prefix: indicates to use the "prefix" scheme and provides the
 base directory for the same
 """
 if user and prefix:
 raise InvalidSchemeCombination("--user", "--prefix")
 if home and prefix:
 raise InvalidSchemeCombination("--home", "--prefix")
 
 if home is not None:
 scheme_name = _infer_home()
 elif user:
 scheme_name = _infer_user()
 else:
 scheme_name = _infer_prefix()
 
 # Special case: When installing into a custom prefix, use posix_prefix
 # instead of osx_framework_library. See _should_use_osx_framework_prefix()
 # docstring for details.
 if prefix is not None and scheme_name == "osx_framework_library":
 scheme_name = "posix_prefix"
 
 if home is not None:
 variables = {k: home for k in _HOME_KEYS}
 elif prefix is not None:
 variables = {k: prefix for k in _HOME_KEYS}
 else:
 variables = {}
 
 paths = sysconfig.get_paths(scheme=scheme_name, vars=variables)
 
 # Logic here is very arbitrary, we're doing it for compatibility, don't ask.
 # 1. Pip historically uses a special header path in virtual environments.
 # 2. If the distribution name is not known, distutils uses 'UNKNOWN'. We
 #    only do the same when not running in a virtual environment because
 #    pip's historical header path logic (see point 1) did not do this.
 if running_under_virtualenv():
 if user:
 base = variables.get("userbase", sys.prefix)
 else:
 base = variables.get("base", sys.prefix)
 python_xy = f"python{get_major_minor_version()}"
 paths["include"] = os.path.join(base, "include", "site", python_xy)
 elif not dist_name:
 dist_name = "UNKNOWN"
 
 scheme = Scheme(
 platlib=paths["platlib"],
 purelib=paths["purelib"],
 headers=os.path.join(paths["include"], dist_name),
 scripts=paths["scripts"],
 data=paths["data"],
 )
 if root is not None:
 for key in SCHEME_KEYS:
 value = distutils.util.change_root(root, getattr(scheme, key))
 setattr(scheme, key, value)
 return scheme
 
 
 def get_bin_prefix() -> str:
 # Forcing to use /usr/local/bin for standard macOS framework installs.
 if sys.platform[:6] == "darwin" and sys.prefix[:16] == "/System/Library/":
 return "/usr/local/bin"
 return sysconfig.get_paths()["scripts"]
 
 
 def get_purelib() -> str:
 return sysconfig.get_paths()["purelib"]
 
 
 def get_platlib() -> str:
 return sysconfig.get_paths()["platlib"]
 
 
 def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]:
 paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix})
 return (paths["purelib"], paths["platlib"])
 
 |