1"""Access to Python's configuration information.""" 2 3import os 4import sys 5from 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: 101if os.name == 'nt': 102 _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv'] 103else: 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. 109def _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 133if _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 185def _safe_realpath(path): 186 try: 187 return realpath(path) 188 except OSError: 189 return path 190 191if sys.executable: 192 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) 193else: 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) 202if _sys_home: 203 _PROJECT_BASE = _sys_home 204 205if 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 216if "_PYTHON_PROJECT_BASE" in os.environ: 217 _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) 218 219def 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 231if _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 244def _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 253def _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 261def _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 279def _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 299def 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 311def get_default_scheme(): 312 return get_preferred_scheme('prefix') 313 314 315def _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 443def 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 456def _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 464def _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 527def _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 535def _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 553def 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 587def 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 599def get_scheme_names(): 600 """Return a tuple containing the schemes names.""" 601 return tuple(sorted(_INSTALL_SCHEMES)) 602 603 604def get_path_names(): 605 """Return a tuple containing the paths names.""" 606 return _SCHEME_KEYS 607 608 609def 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 621def 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 629def 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 709def 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 718def 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 799def get_python_version(): 800 return _PY_VERSION_SHORT 801 802 803def 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 829def _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 836def _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 850if __name__ == '__main__': 851 _main() 852