text: 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
  
  logger = logging.getLogger(__name__)
  
  
  # Notes on _infer_* functions.
  # Unfortunately ``_get_default_scheme()`` is private, 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())
  
  
  def _infer_prefix():
      # type: () -> str
      """Try to find a prefix scheme for the current platform.
  
      This tries:
  
      * 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``.
      """
      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():
      # type: () -> str
      """Try to find a user scheme for the current platform."""
      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():
      # type: () -> str
      """Try to find a home for the current platform."""
      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,  # type: str
      user=False,  # type: bool
      home=None,  # type: typing.Optional[str]
      root=None,  # type: typing.Optional[str]
      isolated=False,  # type: bool
      prefix=None,  # type: typing.Optional[str]
  ):
      # type: (...) -> 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()
  
      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():
      # type: () -> 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():
      # type: () -> str
      return sysconfig.get_paths()["purelib"]
  
  
  def get_platlib():
      # type: () -> str
      return sysconfig.get_paths()["platlib"]
  
  
  def get_prefixed_libs(prefix):
      # type: (str) -> typing.Tuple[str, str]
      paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix})
      return (paths["purelib"], paths["platlib"])
  