python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
pip/
_internal/
locations/
_sysconfig.py
       1  import logging
       2  import os
       3  import sys
       4  import sysconfig
       5  import typing
       6  
       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
      10  
      11  from .base import change_root, get_major_minor_version, is_osx_framework
      12  
      13  logger = 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  
      29  def _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  
      55  def _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  
      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"
      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  
     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:
     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  ]
     120  if sysconfig.get_config_var("userbase") is not None:
     121      _HOME_KEYS.append("userbase")
     122  
     123  
     124  def 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  
     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"]
     206  
     207  
     208  def get_purelib() -> str:
     209      return sysconfig.get_paths()["purelib"]
     210  
     211  
     212  def get_platlib() -> str:
     213      return sysconfig.get_paths()["platlib"]