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