(root)/
Python-3.12.0/
Lib/
sysconfig.py
       1  """Access to Python's configuration information."""
       2  
       3  import os
       4  import sys
       5  import threading
       6  from os.path import realpath
       7  
       8  __all__ = [
       9      'get_config_h_filename',
      10      'get_config_var',
      11      'get_config_vars',
      12      'get_makefile_filename',
      13      'get_path',
      14      'get_path_names',
      15      'get_paths',
      16      'get_platform',
      17      'get_python_version',
      18      'get_scheme_names',
      19      'parse_config_h',
      20  ]
      21  
      22  # Keys for get_config_var() that are never converted to Python integers.
      23  _ALWAYS_STR = {
      24      'MACOSX_DEPLOYMENT_TARGET',
      25  }
      26  
      27  _INSTALL_SCHEMES = {
      28      'posix_prefix': {
      29          'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
      30          'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
      31          'purelib': '{base}/lib/python{py_version_short}/site-packages',
      32          'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
      33          'include':
      34              '{installed_base}/include/python{py_version_short}{abiflags}',
      35          'platinclude':
      36              '{installed_platbase}/include/python{py_version_short}{abiflags}',
      37          'scripts': '{base}/bin',
      38          'data': '{base}',
      39          },
      40      'posix_home': {
      41          'stdlib': '{installed_base}/lib/python',
      42          'platstdlib': '{base}/lib/python',
      43          'purelib': '{base}/lib/python',
      44          'platlib': '{base}/lib/python',
      45          'include': '{installed_base}/include/python',
      46          'platinclude': '{installed_base}/include/python',
      47          'scripts': '{base}/bin',
      48          'data': '{base}',
      49          },
      50      'nt': {
      51          'stdlib': '{installed_base}/Lib',
      52          'platstdlib': '{base}/Lib',
      53          'purelib': '{base}/Lib/site-packages',
      54          'platlib': '{base}/Lib/site-packages',
      55          'include': '{installed_base}/Include',
      56          'platinclude': '{installed_base}/Include',
      57          'scripts': '{base}/Scripts',
      58          'data': '{base}',
      59          },
      60      # Downstream distributors can overwrite the default install scheme.
      61      # This is done to support downstream modifications where distributors change
      62      # the installation layout (eg. different site-packages directory).
      63      # So, distributors will change the default scheme to one that correctly
      64      # represents their layout.
      65      # This presents an issue for projects/people that need to bootstrap virtual
      66      # environments, like virtualenv. As distributors might now be customizing
      67      # the default install scheme, there is no guarantee that the information
      68      # returned by sysconfig.get_default_scheme/get_paths is correct for
      69      # a virtual environment, the only guarantee we have is that it is correct
      70      # for the *current* environment. When bootstrapping a virtual environment,
      71      # we need to know its layout, so that we can place the files in the
      72      # correct locations.
      73      # The "*_venv" install scheme is a scheme to bootstrap virtual environments,
      74      # essentially identical to the default posix_prefix/nt schemes.
      75      # Downstream distributors who patch posix_prefix/nt scheme are encouraged to
      76      # leave the following schemes unchanged
      77      'posix_venv': {
      78          'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
      79          'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
      80          'purelib': '{base}/lib/python{py_version_short}/site-packages',
      81          'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
      82          'include':
      83              '{installed_base}/include/python{py_version_short}{abiflags}',
      84          'platinclude':
      85              '{installed_platbase}/include/python{py_version_short}{abiflags}',
      86          'scripts': '{base}/bin',
      87          'data': '{base}',
      88          },
      89      'nt_venv': {
      90          'stdlib': '{installed_base}/Lib',
      91          'platstdlib': '{base}/Lib',
      92          'purelib': '{base}/Lib/site-packages',
      93          'platlib': '{base}/Lib/site-packages',
      94          'include': '{installed_base}/Include',
      95          'platinclude': '{installed_base}/Include',
      96          'scripts': '{base}/Scripts',
      97          'data': '{base}',
      98          },
      99      }
     100  
     101  # For the OS-native venv scheme, we essentially provide an alias:
     102  if os.name == 'nt':
     103      _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv']
     104  else:
     105      _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv']
     106  
     107  
     108  # NOTE: site.py has copy of this function.
     109  # Sync it when modify this function.
     110  def _getuserbase():
     111      env_base = os.environ.get("PYTHONUSERBASE", None)
     112      if env_base:
     113          return env_base
     114  
     115      # Emscripten, VxWorks, and WASI have no home directories
     116      if sys.platform in {"emscripten", "vxworks", "wasi"}:
     117          return None
     118  
     119      def joinuser(*args):
     120          return os.path.expanduser(os.path.join(*args))
     121  
     122      if os.name == "nt":
     123          base = os.environ.get("APPDATA") or "~"
     124          return joinuser(base, "Python")
     125  
     126      if sys.platform == "darwin" and sys._framework:
     127          return joinuser("~", "Library", sys._framework,
     128                          f"{sys.version_info[0]}.{sys.version_info[1]}")
     129  
     130      return joinuser("~", ".local")
     131  
     132  _HAS_USER_BASE = (_getuserbase() is not None)
     133  
     134  if _HAS_USER_BASE:
     135      _INSTALL_SCHEMES |= {
     136          # NOTE: When modifying "purelib" scheme, update site._get_path() too.
     137          'nt_user': {
     138              'stdlib': '{userbase}/Python{py_version_nodot_plat}',
     139              'platstdlib': '{userbase}/Python{py_version_nodot_plat}',
     140              'purelib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
     141              'platlib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
     142              'include': '{userbase}/Python{py_version_nodot_plat}/Include',
     143              'scripts': '{userbase}/Python{py_version_nodot_plat}/Scripts',
     144              'data': '{userbase}',
     145              },
     146          'posix_user': {
     147              'stdlib': '{userbase}/{platlibdir}/python{py_version_short}',
     148              'platstdlib': '{userbase}/{platlibdir}/python{py_version_short}',
     149              'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
     150              'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
     151              'include': '{userbase}/include/python{py_version_short}',
     152              'scripts': '{userbase}/bin',
     153              'data': '{userbase}',
     154              },
     155          'osx_framework_user': {
     156              'stdlib': '{userbase}/lib/python',
     157              'platstdlib': '{userbase}/lib/python',
     158              'purelib': '{userbase}/lib/python/site-packages',
     159              'platlib': '{userbase}/lib/python/site-packages',
     160              'include': '{userbase}/include/python{py_version_short}',
     161              'scripts': '{userbase}/bin',
     162              'data': '{userbase}',
     163              },
     164      }
     165  
     166  _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
     167                  'scripts', 'data')
     168  
     169  _PY_VERSION = sys.version.split()[0]
     170  _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
     171  _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
     172  _PREFIX = os.path.normpath(sys.prefix)
     173  _BASE_PREFIX = os.path.normpath(sys.base_prefix)
     174  _EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
     175  _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
     176  # Mutex guarding initialization of _CONFIG_VARS.
     177  _CONFIG_VARS_LOCK = threading.RLock()
     178  _CONFIG_VARS = None
     179  # True iff _CONFIG_VARS has been fully initialized.
     180  _CONFIG_VARS_INITIALIZED = False
     181  _USER_BASE = None
     182  
     183  # Regexes needed for parsing Makefile (and similar syntaxes,
     184  # like old-style Setup files).
     185  _variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)"
     186  _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
     187  _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
     188  
     189  
     190  def _safe_realpath(path):
     191      try:
     192          return realpath(path)
     193      except OSError:
     194          return path
     195  
     196  if sys.executable:
     197      _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))
     198  else:
     199      # sys.executable can be empty if argv[0] has been changed and Python is
     200      # unable to retrieve the real program name
     201      _PROJECT_BASE = _safe_realpath(os.getcwd())
     202  
     203  # In a virtual environment, `sys._home` gives us the target directory
     204  # `_PROJECT_BASE` for the executable that created it when the virtual
     205  # python is an actual executable ('venv --copies' or Windows).
     206  _sys_home = getattr(sys, '_home', None)
     207  if _sys_home:
     208      _PROJECT_BASE = _sys_home
     209  
     210  if os.name == 'nt':
     211      # In a source build, the executable is in a subdirectory of the root
     212      # that we want (<root>\PCbuild\<platname>).
     213      # `_BASE_PREFIX` is used as the base installation is where the source
     214      # will be.  The realpath is needed to prevent mount point confusion
     215      # that can occur with just string comparisons.
     216      if _safe_realpath(_PROJECT_BASE).startswith(
     217              _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')):
     218          _PROJECT_BASE = _BASE_PREFIX
     219  
     220  # set for cross builds
     221  if "_PYTHON_PROJECT_BASE" in os.environ:
     222      _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"])
     223  
     224  def is_python_build(check_home=None):
     225      if check_home is not None:
     226          import warnings
     227          warnings.warn("check_home argument is deprecated and ignored.",
     228                        DeprecationWarning, stacklevel=2)
     229      for fn in ("Setup", "Setup.local"):
     230          if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
     231              return True
     232      return False
     233  
     234  _PYTHON_BUILD = is_python_build()
     235  
     236  if _PYTHON_BUILD:
     237      for scheme in ('posix_prefix', 'posix_home'):
     238          # On POSIX-y platforms, Python will:
     239          # - Build from .h files in 'headers' (which is only added to the
     240          #   scheme when building CPython)
     241          # - Install .h files to 'include'
     242          scheme = _INSTALL_SCHEMES[scheme]
     243          scheme['headers'] = scheme['include']
     244          scheme['include'] = '{srcdir}/Include'
     245          scheme['platinclude'] = '{projectbase}/.'
     246      del scheme
     247  
     248  
     249  def _subst_vars(s, local_vars):
     250      try:
     251          return s.format(**local_vars)
     252      except KeyError as var:
     253          try:
     254              return s.format(**os.environ)
     255          except KeyError:
     256              raise AttributeError(f'{var}') from None
     257  
     258  def _extend_dict(target_dict, other_dict):
     259      target_keys = target_dict.keys()
     260      for key, value in other_dict.items():
     261          if key in target_keys:
     262              continue
     263          target_dict[key] = value
     264  
     265  
     266  def _expand_vars(scheme, vars):
     267      res = {}
     268      if vars is None:
     269          vars = {}
     270      _extend_dict(vars, get_config_vars())
     271      if os.name == 'nt':
     272          # On Windows we want to substitute 'lib' for schemes rather
     273          # than the native value (without modifying vars, in case it
     274          # was passed in)
     275          vars = vars | {'platlibdir': 'lib'}
     276  
     277      for key, value in _INSTALL_SCHEMES[scheme].items():
     278          if os.name in ('posix', 'nt'):
     279              value = os.path.expanduser(value)
     280          res[key] = os.path.normpath(_subst_vars(value, vars))
     281      return res
     282  
     283  
     284  def _get_preferred_schemes():
     285      if os.name == 'nt':
     286          return {
     287              'prefix': 'nt',
     288              'home': 'posix_home',
     289              'user': 'nt_user',
     290          }
     291      if sys.platform == 'darwin' and sys._framework:
     292          return {
     293              'prefix': 'posix_prefix',
     294              'home': 'posix_home',
     295              'user': 'osx_framework_user',
     296          }
     297      return {
     298          'prefix': 'posix_prefix',
     299          'home': 'posix_home',
     300          'user': 'posix_user',
     301      }
     302  
     303  
     304  def get_preferred_scheme(key):
     305      if key == 'prefix' and sys.prefix != sys.base_prefix:
     306          return 'venv'
     307      scheme = _get_preferred_schemes()[key]
     308      if scheme not in _INSTALL_SCHEMES:
     309          raise ValueError(
     310              f"{key!r} returned {scheme!r}, which is not a valid scheme "
     311              f"on this platform"
     312          )
     313      return scheme
     314  
     315  
     316  def get_default_scheme():
     317      return get_preferred_scheme('prefix')
     318  
     319  
     320  def _parse_makefile(filename, vars=None, keep_unresolved=True):
     321      """Parse a Makefile-style file.
     322  
     323      A dictionary containing name/value pairs is returned.  If an
     324      optional dictionary is passed in as the second argument, it is
     325      used instead of a new dictionary.
     326      """
     327      import re
     328  
     329      if vars is None:
     330          vars = {}
     331      done = {}
     332      notdone = {}
     333  
     334      with open(filename, encoding=sys.getfilesystemencoding(),
     335                errors="surrogateescape") as f:
     336          lines = f.readlines()
     337  
     338      for line in lines:
     339          if line.startswith('#') or line.strip() == '':
     340              continue
     341          m = re.match(_variable_rx, line)
     342          if m:
     343              n, v = m.group(1, 2)
     344              v = v.strip()
     345              # `$$' is a literal `$' in make
     346              tmpv = v.replace('$$', '')
     347  
     348              if "$" in tmpv:
     349                  notdone[n] = v
     350              else:
     351                  try:
     352                      if n in _ALWAYS_STR:
     353                          raise ValueError
     354  
     355                      v = int(v)
     356                  except ValueError:
     357                      # insert literal `$'
     358                      done[n] = v.replace('$$', '$')
     359                  else:
     360                      done[n] = v
     361  
     362      # do variable interpolation here
     363      variables = list(notdone.keys())
     364  
     365      # Variables with a 'PY_' prefix in the makefile. These need to
     366      # be made available without that prefix through sysconfig.
     367      # Special care is needed to ensure that variable expansion works, even
     368      # if the expansion uses the name without a prefix.
     369      renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
     370  
     371      while len(variables) > 0:
     372          for name in tuple(variables):
     373              value = notdone[name]
     374              m1 = re.search(_findvar1_rx, value)
     375              m2 = re.search(_findvar2_rx, value)
     376              if m1 and m2:
     377                  m = m1 if m1.start() < m2.start() else m2
     378              else:
     379                  m = m1 if m1 else m2
     380              if m is not None:
     381                  n = m.group(1)
     382                  found = True
     383                  if n in done:
     384                      item = str(done[n])
     385                  elif n in notdone:
     386                      # get it on a subsequent round
     387                      found = False
     388                  elif n in os.environ:
     389                      # do it like make: fall back to environment
     390                      item = os.environ[n]
     391  
     392                  elif n in renamed_variables:
     393                      if (name.startswith('PY_') and
     394                          name[3:] in renamed_variables):
     395                          item = ""
     396  
     397                      elif 'PY_' + n in notdone:
     398                          found = False
     399  
     400                      else:
     401                          item = str(done['PY_' + n])
     402  
     403                  else:
     404                      done[n] = item = ""
     405  
     406                  if found:
     407                      after = value[m.end():]
     408                      value = value[:m.start()] + item + after
     409                      if "$" in after:
     410                          notdone[name] = value
     411                      else:
     412                          try:
     413                              if name in _ALWAYS_STR:
     414                                  raise ValueError
     415                              value = int(value)
     416                          except ValueError:
     417                              done[name] = value.strip()
     418                          else:
     419                              done[name] = value
     420                          variables.remove(name)
     421  
     422                          if name.startswith('PY_') \
     423                          and name[3:] in renamed_variables:
     424  
     425                              name = name[3:]
     426                              if name not in done:
     427                                  done[name] = value
     428  
     429              else:
     430                  # Adds unresolved variables to the done dict.
     431                  # This is disabled when called from distutils.sysconfig
     432                  if keep_unresolved:
     433                      done[name] = value
     434                  # bogus variable reference (e.g. "prefix=$/opt/python");
     435                  # just drop it since we can't deal
     436                  variables.remove(name)
     437  
     438      # strip spurious spaces
     439      for k, v in done.items():
     440          if isinstance(v, str):
     441              done[k] = v.strip()
     442  
     443      # save the results in the global dictionary
     444      vars.update(done)
     445      return vars
     446  
     447  
     448  def get_makefile_filename():
     449      """Return the path of the Makefile."""
     450      if _PYTHON_BUILD:
     451          return os.path.join(_PROJECT_BASE, "Makefile")
     452      if hasattr(sys, 'abiflags'):
     453          config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}'
     454      else:
     455          config_dir_name = 'config'
     456      if hasattr(sys.implementation, '_multiarch'):
     457          config_dir_name += f'-{sys.implementation._multiarch}'
     458      return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')
     459  
     460  
     461  def _get_sysconfigdata_name():
     462      multiarch = getattr(sys.implementation, '_multiarch', '')
     463      return os.environ.get(
     464          '_PYTHON_SYSCONFIGDATA_NAME',
     465          f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
     466      )
     467  
     468  
     469  def _generate_posix_vars():
     470      """Generate the Python module containing build-time variables."""
     471      import pprint
     472      vars = {}
     473      # load the installed Makefile:
     474      makefile = get_makefile_filename()
     475      try:
     476          _parse_makefile(makefile, vars)
     477      except OSError as e:
     478          msg = f"invalid Python installation: unable to open {makefile}"
     479          if hasattr(e, "strerror"):
     480              msg = f"{msg} ({e.strerror})"
     481          raise OSError(msg)
     482      # load the installed pyconfig.h:
     483      config_h = get_config_h_filename()
     484      try:
     485          with open(config_h, encoding="utf-8") as f:
     486              parse_config_h(f, vars)
     487      except OSError as e:
     488          msg = f"invalid Python installation: unable to open {config_h}"
     489          if hasattr(e, "strerror"):
     490              msg = f"{msg} ({e.strerror})"
     491          raise OSError(msg)
     492      # On AIX, there are wrong paths to the linker scripts in the Makefile
     493      # -- these paths are relative to the Python source, but when installed
     494      # the scripts are in another directory.
     495      if _PYTHON_BUILD:
     496          vars['BLDSHARED'] = vars['LDSHARED']
     497  
     498      # There's a chicken-and-egg situation on OS X with regards to the
     499      # _sysconfigdata module after the changes introduced by #15298:
     500      # get_config_vars() is called by get_platform() as part of the
     501      # `make pybuilddir.txt` target -- which is a precursor to the
     502      # _sysconfigdata.py module being constructed.  Unfortunately,
     503      # get_config_vars() eventually calls _init_posix(), which attempts
     504      # to import _sysconfigdata, which we won't have built yet.  In order
     505      # for _init_posix() to work, if we're on Darwin, just mock up the
     506      # _sysconfigdata module manually and populate it with the build vars.
     507      # This is more than sufficient for ensuring the subsequent call to
     508      # get_platform() succeeds.
     509      name = _get_sysconfigdata_name()
     510      if 'darwin' in sys.platform:
     511          import types
     512          module = types.ModuleType(name)
     513          module.build_time_vars = vars
     514          sys.modules[name] = module
     515  
     516      pybuilddir = f'build/lib.{get_platform()}-{_PY_VERSION_SHORT}'
     517      if hasattr(sys, "gettotalrefcount"):
     518          pybuilddir += '-pydebug'
     519      os.makedirs(pybuilddir, exist_ok=True)
     520      destfile = os.path.join(pybuilddir, name + '.py')
     521  
     522      with open(destfile, 'w', encoding='utf8') as f:
     523          f.write('# system configuration generated and used by'
     524                  ' the sysconfig module\n')
     525          f.write('build_time_vars = ')
     526          pprint.pprint(vars, stream=f)
     527  
     528      # Create file used for sys.path fixup -- see Modules/getpath.c
     529      with open('pybuilddir.txt', 'w', encoding='utf8') as f:
     530          f.write(pybuilddir)
     531  
     532  def _init_posix(vars):
     533      """Initialize the module as appropriate for POSIX systems."""
     534      # _sysconfigdata is generated at build time, see _generate_posix_vars()
     535      name = _get_sysconfigdata_name()
     536      _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
     537      build_time_vars = _temp.build_time_vars
     538      vars.update(build_time_vars)
     539  
     540  def _init_non_posix(vars):
     541      """Initialize the module as appropriate for NT"""
     542      # set basic install directories
     543      import _imp
     544      vars['LIBDEST'] = get_path('stdlib')
     545      vars['BINLIBDEST'] = get_path('platstdlib')
     546      vars['INCLUDEPY'] = get_path('include')
     547      try:
     548          # GH-99201: _imp.extension_suffixes may be empty when
     549          # HAVE_DYNAMIC_LOADING is not set. In this case, don't set EXT_SUFFIX.
     550          vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
     551      except IndexError:
     552          pass
     553      vars['EXE'] = '.exe'
     554      vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
     555      vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
     556      vars['TZPATH'] = ''
     557  
     558  #
     559  # public APIs
     560  #
     561  
     562  
     563  def parse_config_h(fp, vars=None):
     564      """Parse a config.h-style file.
     565  
     566      A dictionary containing name/value pairs is returned.  If an
     567      optional dictionary is passed in as the second argument, it is
     568      used instead of a new dictionary.
     569      """
     570      if vars is None:
     571          vars = {}
     572      import re
     573      define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
     574      undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
     575  
     576      while True:
     577          line = fp.readline()
     578          if not line:
     579              break
     580          m = define_rx.match(line)
     581          if m:
     582              n, v = m.group(1, 2)
     583              try:
     584                  if n in _ALWAYS_STR:
     585                      raise ValueError
     586                  v = int(v)
     587              except ValueError:
     588                  pass
     589              vars[n] = v
     590          else:
     591              m = undef_rx.match(line)
     592              if m:
     593                  vars[m.group(1)] = 0
     594      return vars
     595  
     596  
     597  def get_config_h_filename():
     598      """Return the path of pyconfig.h."""
     599      if _PYTHON_BUILD:
     600          if os.name == "nt":
     601              inc_dir = os.path.join(_PROJECT_BASE, "PC")
     602          else:
     603              inc_dir = _PROJECT_BASE
     604      else:
     605          inc_dir = get_path('platinclude')
     606      return os.path.join(inc_dir, 'pyconfig.h')
     607  
     608  
     609  def get_scheme_names():
     610      """Return a tuple containing the schemes names."""
     611      return tuple(sorted(_INSTALL_SCHEMES))
     612  
     613  
     614  def get_path_names():
     615      """Return a tuple containing the paths names."""
     616      return _SCHEME_KEYS
     617  
     618  
     619  def get_paths(scheme=get_default_scheme(), vars=None, expand=True):
     620      """Return a mapping containing an install scheme.
     621  
     622      ``scheme`` is the install scheme name. If not provided, it will
     623      return the default scheme for the current platform.
     624      """
     625      if expand:
     626          return _expand_vars(scheme, vars)
     627      else:
     628          return _INSTALL_SCHEMES[scheme]
     629  
     630  
     631  def get_path(name, scheme=get_default_scheme(), vars=None, expand=True):
     632      """Return a path corresponding to the scheme.
     633  
     634      ``scheme`` is the install scheme name.
     635      """
     636      return get_paths(scheme, vars, expand)[name]
     637  
     638  
     639  def _init_config_vars():
     640      global _CONFIG_VARS
     641      _CONFIG_VARS = {}
     642      # Normalized versions of prefix and exec_prefix are handy to have;
     643      # in fact, these are the standard versions used most places in the
     644      # Distutils.
     645      _CONFIG_VARS['prefix'] = _PREFIX
     646      _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
     647      _CONFIG_VARS['py_version'] = _PY_VERSION
     648      _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
     649      _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT
     650      _CONFIG_VARS['installed_base'] = _BASE_PREFIX
     651      _CONFIG_VARS['base'] = _PREFIX
     652      _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX
     653      _CONFIG_VARS['platbase'] = _EXEC_PREFIX
     654      _CONFIG_VARS['projectbase'] = _PROJECT_BASE
     655      _CONFIG_VARS['platlibdir'] = sys.platlibdir
     656      try:
     657          _CONFIG_VARS['abiflags'] = sys.abiflags
     658      except AttributeError:
     659          # sys.abiflags may not be defined on all platforms.
     660          _CONFIG_VARS['abiflags'] = ''
     661      try:
     662          _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '')
     663      except AttributeError:
     664          _CONFIG_VARS['py_version_nodot_plat'] = ''
     665  
     666      if os.name == 'nt':
     667          _init_non_posix(_CONFIG_VARS)
     668          _CONFIG_VARS['VPATH'] = sys._vpath
     669      if os.name == 'posix':
     670          _init_posix(_CONFIG_VARS)
     671      if _HAS_USER_BASE:
     672          # Setting 'userbase' is done below the call to the
     673          # init function to enable using 'get_config_var' in
     674          # the init-function.
     675          _CONFIG_VARS['userbase'] = _getuserbase()
     676  
     677      # Always convert srcdir to an absolute path
     678      srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE)
     679      if os.name == 'posix':
     680          if _PYTHON_BUILD:
     681              # If srcdir is a relative path (typically '.' or '..')
     682              # then it should be interpreted relative to the directory
     683              # containing Makefile.
     684              base = os.path.dirname(get_makefile_filename())
     685              srcdir = os.path.join(base, srcdir)
     686          else:
     687              # srcdir is not meaningful since the installation is
     688              # spread about the filesystem.  We choose the
     689              # directory containing the Makefile since we know it
     690              # exists.
     691              srcdir = os.path.dirname(get_makefile_filename())
     692      _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir)
     693  
     694      # OS X platforms require special customization to handle
     695      # multi-architecture, multi-os-version installers
     696      if sys.platform == 'darwin':
     697          import _osx_support
     698          _osx_support.customize_config_vars(_CONFIG_VARS)
     699  
     700      global _CONFIG_VARS_INITIALIZED
     701      _CONFIG_VARS_INITIALIZED = True
     702  
     703  
     704  def get_config_vars(*args):
     705      """With no arguments, return a dictionary of all configuration
     706      variables relevant for the current platform.
     707  
     708      On Unix, this means every variable defined in Python's installed Makefile;
     709      On Windows it's a much smaller set.
     710  
     711      With arguments, return a list of values that result from looking up
     712      each argument in the configuration variable dictionary.
     713      """
     714  
     715      # Avoid claiming the lock once initialization is complete.
     716      if not _CONFIG_VARS_INITIALIZED:
     717          with _CONFIG_VARS_LOCK:
     718              # Test again with the lock held to avoid races. Note that
     719              # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,
     720              # to ensure that recursive calls to get_config_vars()
     721              # don't re-enter init_config_vars().
     722              if _CONFIG_VARS is None:
     723                  _init_config_vars()
     724  
     725      if args:
     726          vals = []
     727          for name in args:
     728              vals.append(_CONFIG_VARS.get(name))
     729          return vals
     730      else:
     731          return _CONFIG_VARS
     732  
     733  
     734  def get_config_var(name):
     735      """Return the value of a single variable using the dictionary returned by
     736      'get_config_vars()'.
     737  
     738      Equivalent to get_config_vars().get(name)
     739      """
     740      return get_config_vars().get(name)
     741  
     742  
     743  def get_platform():
     744      """Return a string that identifies the current platform.
     745  
     746      This is used mainly to distinguish platform-specific build directories and
     747      platform-specific built distributions.  Typically includes the OS name and
     748      version and the architecture (as supplied by 'os.uname()'), although the
     749      exact information included depends on the OS; on Linux, the kernel version
     750      isn't particularly important.
     751  
     752      Examples of returned values:
     753         linux-i586
     754         linux-alpha (?)
     755         solaris-2.6-sun4u
     756  
     757      Windows will return one of:
     758         win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
     759         win32 (all others - specifically, sys.platform is returned)
     760  
     761      For other non-POSIX platforms, currently just returns 'sys.platform'.
     762  
     763      """
     764      if os.name == 'nt':
     765          if 'amd64' in sys.version.lower():
     766              return 'win-amd64'
     767          if '(arm)' in sys.version.lower():
     768              return 'win-arm32'
     769          if '(arm64)' in sys.version.lower():
     770              return 'win-arm64'
     771          return sys.platform
     772  
     773      if os.name != "posix" or not hasattr(os, 'uname'):
     774          # XXX what about the architecture? NT is Intel or Alpha
     775          return sys.platform
     776  
     777      # Set for cross builds explicitly
     778      if "_PYTHON_HOST_PLATFORM" in os.environ:
     779          return os.environ["_PYTHON_HOST_PLATFORM"]
     780  
     781      # Try to distinguish various flavours of Unix
     782      osname, host, release, version, machine = os.uname()
     783  
     784      # Convert the OS name to lowercase, remove '/' characters, and translate
     785      # spaces (for "Power Macintosh")
     786      osname = osname.lower().replace('/', '')
     787      machine = machine.replace(' ', '_')
     788      machine = machine.replace('/', '-')
     789  
     790      if osname[:5] == "linux":
     791          # At least on Linux/Intel, 'machine' is the processor --
     792          # i386, etc.
     793          # XXX what about Alpha, SPARC, etc?
     794          return  f"{osname}-{machine}"
     795      elif osname[:5] == "sunos":
     796          if release[0] >= "5":           # SunOS 5 == Solaris 2
     797              osname = "solaris"
     798              release = f"{int(release[0]) - 3}.{release[2:]}"
     799              # We can't use "platform.architecture()[0]" because a
     800              # bootstrap problem. We use a dict to get an error
     801              # if some suspicious happens.
     802              bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
     803              machine += f".{bitness[sys.maxsize]}"
     804          # fall through to standard osname-release-machine representation
     805      elif osname[:3] == "aix":
     806          from _aix_support import aix_platform
     807          return aix_platform()
     808      elif osname[:6] == "cygwin":
     809          osname = "cygwin"
     810          import re
     811          rel_re = re.compile(r'[\d.]+')
     812          m = rel_re.match(release)
     813          if m:
     814              release = m.group()
     815      elif osname[:6] == "darwin":
     816          import _osx_support
     817          osname, release, machine = _osx_support.get_platform_osx(
     818                                              get_config_vars(),
     819                                              osname, release, machine)
     820  
     821      return f"{osname}-{release}-{machine}"
     822  
     823  
     824  def get_python_version():
     825      return _PY_VERSION_SHORT
     826  
     827  
     828  def expand_makefile_vars(s, vars):
     829      """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
     830      'string' according to 'vars' (a dictionary mapping variable names to
     831      values).  Variables not present in 'vars' are silently expanded to the
     832      empty string.  The variable values in 'vars' should not contain further
     833      variable expansions; if 'vars' is the output of 'parse_makefile()',
     834      you're fine.  Returns a variable-expanded version of 's'.
     835      """
     836      import re
     837  
     838      # This algorithm does multiple expansion, so if vars['foo'] contains
     839      # "${bar}", it will expand ${foo} to ${bar}, and then expand
     840      # ${bar}... and so forth.  This is fine as long as 'vars' comes from
     841      # 'parse_makefile()', which takes care of such expansions eagerly,
     842      # according to make's variable expansion semantics.
     843  
     844      while True:
     845          m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s)
     846          if m:
     847              (beg, end) = m.span()
     848              s = s[0:beg] + vars.get(m.group(1)) + s[end:]
     849          else:
     850              break
     851      return s
     852  
     853  
     854  def _print_dict(title, data):
     855      for index, (key, value) in enumerate(sorted(data.items())):
     856          if index == 0:
     857              print(f'{title}: ')
     858          print(f'\t{key} = "{value}"')
     859  
     860  
     861  def _main():
     862      """Display all information sysconfig detains."""
     863      if '--generate-posix-vars' in sys.argv:
     864          _generate_posix_vars()
     865          return
     866      print(f'Platform: "{get_platform()}"')
     867      print(f'Python version: "{get_python_version()}"')
     868      print(f'Current installation scheme: "{get_default_scheme()}"')
     869      print()
     870      _print_dict('Paths', get_paths())
     871      print()
     872      _print_dict('Variables', get_config_vars())
     873  
     874  
     875  if __name__ == '__main__':
     876      _main()