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