1"""Access to Python's configuration information.""" 2 3import os 4import sys 5import threading 6from 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 'IPHONEOS_DEPLOYMENT_TARGET', 25 'MACOSX_DEPLOYMENT_TARGET', 26} 27 28_INSTALL_SCHEMES = { 29 'posix_prefix': { 30 'stdlib': '{installed_base}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 31 'platstdlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 32 'purelib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 33 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 34 'include': 35 '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', 36 'platinclude': 37 '{installed_platbase}/include/{implementation_lower}{py_version_short}{abiflags}', 38 'scripts': '{base}/bin', 39 'data': '{base}', 40 }, 41 'posix_home': { 42 'stdlib': '{installed_base}/lib/{implementation_lower}', 43 'platstdlib': '{base}/lib/{implementation_lower}', 44 'purelib': '{base}/lib/{implementation_lower}', 45 'platlib': '{base}/lib/{implementation_lower}', 46 'include': '{installed_base}/include/{implementation_lower}', 47 'platinclude': '{installed_base}/include/{implementation_lower}', 48 'scripts': '{base}/bin', 49 'data': '{base}', 50 }, 51 'nt': { 52 'stdlib': '{installed_base}/Lib', 53 'platstdlib': '{base}/Lib', 54 'purelib': '{base}/Lib/site-packages', 55 'platlib': '{base}/Lib/site-packages', 56 'include': '{installed_base}/Include', 57 'platinclude': '{installed_base}/Include', 58 'scripts': '{base}/Scripts', 59 'data': '{base}', 60 }, 61 62 # Downstream distributors can overwrite the default install scheme. 63 # This is done to support downstream modifications where distributors change 64 # the installation layout (eg. different site-packages directory). 65 # So, distributors will change the default scheme to one that correctly 66 # represents their layout. 67 # This presents an issue for projects/people that need to bootstrap virtual 68 # environments, like virtualenv. As distributors might now be customizing 69 # the default install scheme, there is no guarantee that the information 70 # returned by sysconfig.get_default_scheme/get_paths is correct for 71 # a virtual environment, the only guarantee we have is that it is correct 72 # for the *current* environment. When bootstrapping a virtual environment, 73 # we need to know its layout, so that we can place the files in the 74 # correct locations. 75 # The "*_venv" install scheme is a scheme to bootstrap virtual environments, 76 # essentially identical to the default posix_prefix/nt schemes. 77 # Downstream distributors who patch posix_prefix/nt scheme are encouraged to 78 # leave the following schemes unchanged 79 'posix_venv': { 80 'stdlib': '{installed_base}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 81 'platstdlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 82 'purelib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 83 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 84 'include': 85 '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', 86 'platinclude': 87 '{installed_platbase}/include/{implementation_lower}{py_version_short}{abiflags}', 88 'scripts': '{base}/bin', 89 'data': '{base}', 90 }, 91 'nt_venv': { 92 'stdlib': '{installed_base}/Lib', 93 'platstdlib': '{base}/Lib', 94 'purelib': '{base}/Lib/site-packages', 95 'platlib': '{base}/Lib/site-packages', 96 'include': '{installed_base}/Include', 97 'platinclude': '{installed_base}/Include', 98 'scripts': '{base}/Scripts', 99 'data': '{base}', 100 }, 101 } 102 103# For the OS-native venv scheme, we essentially provide an alias: 104if os.name == 'nt': 105 _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv'] 106else: 107 _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] 108 109def _get_implementation(): 110 return 'Python' 111 112# NOTE: site.py has copy of this function. 113# Sync it when modify this function. 114def _getuserbase(): 115 env_base = os.environ.get("PYTHONUSERBASE", None) 116 if env_base: 117 return env_base 118 119 # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories 120 if sys.platform in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: 121 return None 122 123 def joinuser(*args): 124 return os.path.expanduser(os.path.join(*args)) 125 126 if os.name == "nt": 127 base = os.environ.get("APPDATA") or "~" 128 return joinuser(base, _get_implementation()) 129 130 if sys.platform == "darwin" and sys._framework: 131 return joinuser("~", "Library", sys._framework, 132 f"{sys.version_info[0]}.{sys.version_info[1]}") 133 134 return joinuser("~", ".local") 135 136_HAS_USER_BASE = (_getuserbase() is not None) 137 138if _HAS_USER_BASE: 139 _INSTALL_SCHEMES |= { 140 # NOTE: When modifying "purelib" scheme, update site._get_path() too. 141 'nt_user': { 142 'stdlib': '{userbase}/{implementation}{py_version_nodot_plat}', 143 'platstdlib': '{userbase}/{implementation}{py_version_nodot_plat}', 144 'purelib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', 145 'platlib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', 146 'include': '{userbase}/{implementation}{py_version_nodot_plat}/Include', 147 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts', 148 'data': '{userbase}', 149 }, 150 'posix_user': { 151 'stdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 152 'platstdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', 153 'purelib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 154 'platlib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', 155 'include': '{userbase}/include/{implementation_lower}{py_version_short}{abi_thread}', 156 'scripts': '{userbase}/bin', 157 'data': '{userbase}', 158 }, 159 'osx_framework_user': { 160 'stdlib': '{userbase}/lib/{implementation_lower}', 161 'platstdlib': '{userbase}/lib/{implementation_lower}', 162 'purelib': '{userbase}/lib/{implementation_lower}/site-packages', 163 'platlib': '{userbase}/lib/{implementation_lower}/site-packages', 164 'include': '{userbase}/include/{implementation_lower}{py_version_short}', 165 'scripts': '{userbase}/bin', 166 'data': '{userbase}', 167 }, 168 } 169 170_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', 171 'scripts', 'data') 172 173_PY_VERSION = sys.version.split()[0] 174_PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}' 175_PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}' 176_BASE_PREFIX = os.path.normpath(sys.base_prefix) 177_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) 178# Mutex guarding initialization of _CONFIG_VARS. 179_CONFIG_VARS_LOCK = threading.RLock() 180_CONFIG_VARS = None 181# True iff _CONFIG_VARS has been fully initialized. 182_CONFIG_VARS_INITIALIZED = False 183_USER_BASE = None 184 185 186def _safe_realpath(path): 187 try: 188 return realpath(path) 189 except OSError: 190 return path 191 192if sys.executable: 193 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) 194else: 195 # sys.executable can be empty if argv[0] has been changed and Python is 196 # unable to retrieve the real program name 197 _PROJECT_BASE = _safe_realpath(os.getcwd()) 198 199# In a virtual environment, `sys._home` gives us the target directory 200# `_PROJECT_BASE` for the executable that created it when the virtual 201# python is an actual executable ('venv --copies' or Windows). 202_sys_home = getattr(sys, '_home', None) 203if _sys_home: 204 _PROJECT_BASE = _sys_home 205 206if os.name == 'nt': 207 # In a source build, the executable is in a subdirectory of the root 208 # that we want (<root>\PCbuild\<platname>). 209 # `_BASE_PREFIX` is used as the base installation is where the source 210 # will be. The realpath is needed to prevent mount point confusion 211 # that can occur with just string comparisons. 212 if _safe_realpath(_PROJECT_BASE).startswith( 213 _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')): 214 _PROJECT_BASE = _BASE_PREFIX 215 216# set for cross builds 217if "_PYTHON_PROJECT_BASE" in os.environ: 218 _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) 219 220def is_python_build(check_home=None): 221 if check_home is not None: 222 import warnings 223 warnings.warn("check_home argument is deprecated and ignored.", 224 DeprecationWarning, stacklevel=2) 225 for fn in ("Setup", "Setup.local"): 226 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): 227 return True 228 return False 229 230_PYTHON_BUILD = is_python_build() 231 232if _PYTHON_BUILD: 233 for scheme in ('posix_prefix', 'posix_home'): 234 # On POSIX-y platforms, Python will: 235 # - Build from .h files in 'headers' (which is only added to the 236 # scheme when building CPython) 237 # - Install .h files to 'include' 238 scheme = _INSTALL_SCHEMES[scheme] 239 scheme['headers'] = scheme['include'] 240 scheme['include'] = '{srcdir}/Include' 241 scheme['platinclude'] = '{projectbase}/.' 242 del scheme 243 244 245def _subst_vars(s, local_vars): 246 try: 247 return s.format(**local_vars) 248 except KeyError as var: 249 try: 250 return s.format(**os.environ) 251 except KeyError: 252 raise AttributeError(f'{var}') from None 253 254def _extend_dict(target_dict, other_dict): 255 target_keys = target_dict.keys() 256 for key, value in other_dict.items(): 257 if key in target_keys: 258 continue 259 target_dict[key] = value 260 261 262def _expand_vars(scheme, vars): 263 res = {} 264 if vars is None: 265 vars = {} 266 _extend_dict(vars, get_config_vars()) 267 if os.name == 'nt': 268 # On Windows we want to substitute 'lib' for schemes rather 269 # than the native value (without modifying vars, in case it 270 # was passed in) 271 vars = vars | {'platlibdir': 'lib'} 272 273 for key, value in _INSTALL_SCHEMES[scheme].items(): 274 if os.name in ('posix', 'nt'): 275 value = os.path.expanduser(value) 276 res[key] = os.path.normpath(_subst_vars(value, vars)) 277 return res 278 279 280def _get_preferred_schemes(): 281 if os.name == 'nt': 282 return { 283 'prefix': 'nt', 284 'home': 'posix_home', 285 'user': 'nt_user', 286 } 287 if sys.platform == 'darwin' and sys._framework: 288 return { 289 'prefix': 'posix_prefix', 290 'home': 'posix_home', 291 'user': 'osx_framework_user', 292 } 293 294 return { 295 'prefix': 'posix_prefix', 296 'home': 'posix_home', 297 'user': 'posix_user', 298 } 299 300 301def get_preferred_scheme(key): 302 if key == 'prefix' and sys.prefix != sys.base_prefix: 303 return 'venv' 304 scheme = _get_preferred_schemes()[key] 305 if scheme not in _INSTALL_SCHEMES: 306 raise ValueError( 307 f"{key!r} returned {scheme!r}, which is not a valid scheme " 308 f"on this platform" 309 ) 310 return scheme 311 312 313def get_default_scheme(): 314 return get_preferred_scheme('prefix') 315 316 317def get_makefile_filename(): 318 """Return the path of the Makefile.""" 319 if _PYTHON_BUILD: 320 return os.path.join(_PROJECT_BASE, "Makefile") 321 if hasattr(sys, 'abiflags'): 322 config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}' 323 else: 324 config_dir_name = 'config' 325 if hasattr(sys.implementation, '_multiarch'): 326 config_dir_name += f'-{sys.implementation._multiarch}' 327 return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') 328 329 330def _get_sysconfigdata_name(): 331 multiarch = getattr(sys.implementation, '_multiarch', '') 332 return os.environ.get( 333 '_PYTHON_SYSCONFIGDATA_NAME', 334 f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}', 335 ) 336 337def _init_posix(vars): 338 """Initialize the module as appropriate for POSIX systems.""" 339 # _sysconfigdata is generated at build time, see _generate_posix_vars() 340 name = _get_sysconfigdata_name() 341 342 # For cross builds, the path to the target's sysconfigdata must be specified 343 # so it can be imported. It cannot be in PYTHONPATH, as foreign modules in 344 # sys.path can cause crashes when loaded by the host interpreter. 345 # Rely on truthiness as a valueless env variable is still an empty string. 346 # See OS X note in _generate_posix_vars re _sysconfigdata. 347 if (path := os.environ.get('_PYTHON_SYSCONFIGDATA_PATH')): 348 from importlib.machinery import FileFinder, SourceFileLoader, SOURCE_SUFFIXES 349 from importlib.util import module_from_spec 350 spec = FileFinder(path, (SourceFileLoader, SOURCE_SUFFIXES)).find_spec(name) 351 _temp = module_from_spec(spec) 352 spec.loader.exec_module(_temp) 353 else: 354 _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) 355 build_time_vars = _temp.build_time_vars 356 vars.update(build_time_vars) 357 358def _init_non_posix(vars): 359 """Initialize the module as appropriate for NT""" 360 # set basic install directories 361 import _winapi 362 import _sysconfig 363 vars['LIBDEST'] = get_path('stdlib') 364 vars['BINLIBDEST'] = get_path('platstdlib') 365 vars['INCLUDEPY'] = get_path('include') 366 367 # Add EXT_SUFFIX, SOABI, and Py_GIL_DISABLED 368 vars.update(_sysconfig.config_vars()) 369 370 vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs')) 371 if hasattr(sys, 'dllhandle'): 372 dllhandle = _winapi.GetModuleFileName(sys.dllhandle) 373 vars['LIBRARY'] = os.path.basename(_safe_realpath(dllhandle)) 374 vars['LDLIBRARY'] = vars['LIBRARY'] 375 vars['EXE'] = '.exe' 376 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT 377 vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) 378 vars['TZPATH'] = '' 379 380# 381# public APIs 382# 383 384 385def parse_config_h(fp, vars=None): 386 """Parse a config.h-style file. 387 388 A dictionary containing name/value pairs is returned. If an 389 optional dictionary is passed in as the second argument, it is 390 used instead of a new dictionary. 391 """ 392 if vars is None: 393 vars = {} 394 import re 395 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") 396 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") 397 398 while True: 399 line = fp.readline() 400 if not line: 401 break 402 m = define_rx.match(line) 403 if m: 404 n, v = m.group(1, 2) 405 try: 406 if n in _ALWAYS_STR: 407 raise ValueError 408 v = int(v) 409 except ValueError: 410 pass 411 vars[n] = v 412 else: 413 m = undef_rx.match(line) 414 if m: 415 vars[m.group(1)] = 0 416 return vars 417 418 419def get_config_h_filename(): 420 """Return the path of pyconfig.h.""" 421 if _PYTHON_BUILD: 422 if os.name == "nt": 423 inc_dir = os.path.dirname(sys._base_executable) 424 else: 425 inc_dir = _PROJECT_BASE 426 else: 427 inc_dir = get_path('platinclude') 428 return os.path.join(inc_dir, 'pyconfig.h') 429 430 431def get_scheme_names(): 432 """Return a tuple containing the schemes names.""" 433 return tuple(sorted(_INSTALL_SCHEMES)) 434 435 436def get_path_names(): 437 """Return a tuple containing the paths names.""" 438 return _SCHEME_KEYS 439 440 441def get_paths(scheme=get_default_scheme(), vars=None, expand=True): 442 """Return a mapping containing an install scheme. 443 444 ``scheme`` is the install scheme name. If not provided, it will 445 return the default scheme for the current platform. 446 """ 447 if expand: 448 return _expand_vars(scheme, vars) 449 else: 450 return _INSTALL_SCHEMES[scheme] 451 452 453def get_path(name, scheme=get_default_scheme(), vars=None, expand=True): 454 """Return a path corresponding to the scheme. 455 456 ``scheme`` is the install scheme name. 457 """ 458 return get_paths(scheme, vars, expand)[name] 459 460 461def _init_config_vars(): 462 global _CONFIG_VARS 463 _CONFIG_VARS = {} 464 # Normalized versions of prefix and exec_prefix are handy to have; 465 # in fact, these are the standard versions used most places in the 466 # Distutils. 467 _PREFIX = os.path.normpath(sys.prefix) 468 _EXEC_PREFIX = os.path.normpath(sys.exec_prefix) 469 _CONFIG_VARS['prefix'] = _PREFIX # FIXME: This gets overwriten by _init_posix. 470 _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix. 471 _CONFIG_VARS['py_version'] = _PY_VERSION 472 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT 473 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT 474 _CONFIG_VARS['installed_base'] = _BASE_PREFIX 475 _CONFIG_VARS['base'] = _PREFIX 476 _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX 477 _CONFIG_VARS['platbase'] = _EXEC_PREFIX 478 _CONFIG_VARS['projectbase'] = _PROJECT_BASE 479 _CONFIG_VARS['platlibdir'] = sys.platlibdir 480 _CONFIG_VARS['implementation'] = _get_implementation() 481 _CONFIG_VARS['implementation_lower'] = _get_implementation().lower() 482 try: 483 _CONFIG_VARS['abiflags'] = sys.abiflags 484 except AttributeError: 485 # sys.abiflags may not be defined on all platforms. 486 _CONFIG_VARS['abiflags'] = '' 487 try: 488 _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') 489 except AttributeError: 490 _CONFIG_VARS['py_version_nodot_plat'] = '' 491 492 if os.name == 'nt': 493 _init_non_posix(_CONFIG_VARS) 494 _CONFIG_VARS['VPATH'] = sys._vpath 495 if os.name == 'posix': 496 _init_posix(_CONFIG_VARS) 497 if _HAS_USER_BASE: 498 # Setting 'userbase' is done below the call to the 499 # init function to enable using 'get_config_var' in 500 # the init-function. 501 _CONFIG_VARS['userbase'] = _getuserbase() 502 503 # e.g., 't' for free-threaded or '' for default build 504 _CONFIG_VARS['abi_thread'] = 't' if _CONFIG_VARS.get('Py_GIL_DISABLED') else '' 505 506 # Always convert srcdir to an absolute path 507 srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) 508 if os.name == 'posix': 509 if _PYTHON_BUILD: 510 # If srcdir is a relative path (typically '.' or '..') 511 # then it should be interpreted relative to the directory 512 # containing Makefile. 513 base = os.path.dirname(get_makefile_filename()) 514 srcdir = os.path.join(base, srcdir) 515 else: 516 # srcdir is not meaningful since the installation is 517 # spread about the filesystem. We choose the 518 # directory containing the Makefile since we know it 519 # exists. 520 srcdir = os.path.dirname(get_makefile_filename()) 521 _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) 522 523 # OS X platforms require special customization to handle 524 # multi-architecture, multi-os-version installers 525 if sys.platform == 'darwin': 526 import _osx_support 527 _osx_support.customize_config_vars(_CONFIG_VARS) 528 529 global _CONFIG_VARS_INITIALIZED 530 _CONFIG_VARS_INITIALIZED = True 531 532 533def get_config_vars(*args): 534 """With no arguments, return a dictionary of all configuration 535 variables relevant for the current platform. 536 537 On Unix, this means every variable defined in Python's installed Makefile; 538 On Windows it's a much smaller set. 539 540 With arguments, return a list of values that result from looking up 541 each argument in the configuration variable dictionary. 542 """ 543 global _CONFIG_VARS_INITIALIZED 544 545 # Avoid claiming the lock once initialization is complete. 546 if not _CONFIG_VARS_INITIALIZED: 547 with _CONFIG_VARS_LOCK: 548 # Test again with the lock held to avoid races. Note that 549 # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED, 550 # to ensure that recursive calls to get_config_vars() 551 # don't re-enter init_config_vars(). 552 if _CONFIG_VARS is None: 553 _init_config_vars() 554 else: 555 # If the site module initialization happened after _CONFIG_VARS was 556 # initialized, a virtual environment might have been activated, resulting in 557 # variables like sys.prefix changing their value, so we need to re-init the 558 # config vars (see GH-126789). 559 if _CONFIG_VARS['base'] != os.path.normpath(sys.prefix): 560 with _CONFIG_VARS_LOCK: 561 _CONFIG_VARS_INITIALIZED = False 562 _init_config_vars() 563 564 if args: 565 vals = [] 566 for name in args: 567 vals.append(_CONFIG_VARS.get(name)) 568 return vals 569 else: 570 return _CONFIG_VARS 571 572 573def get_config_var(name): 574 """Return the value of a single variable using the dictionary returned by 575 'get_config_vars()'. 576 577 Equivalent to get_config_vars().get(name) 578 """ 579 return get_config_vars().get(name) 580 581 582def get_platform(): 583 """Return a string that identifies the current platform. 584 585 This is used mainly to distinguish platform-specific build directories and 586 platform-specific built distributions. Typically includes the OS name and 587 version and the architecture (as supplied by 'os.uname()'), although the 588 exact information included depends on the OS; on Linux, the kernel version 589 isn't particularly important. 590 591 Examples of returned values: 592 linux-i586 593 linux-alpha (?) 594 solaris-2.6-sun4u 595 596 Windows will return one of: 597 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) 598 win32 (all others - specifically, sys.platform is returned) 599 600 For other non-POSIX platforms, currently just returns 'sys.platform'. 601 602 """ 603 if os.name == 'nt': 604 if 'amd64' in sys.version.lower(): 605 return 'win-amd64' 606 if '(arm)' in sys.version.lower(): 607 return 'win-arm32' 608 if '(arm64)' in sys.version.lower(): 609 return 'win-arm64' 610 return sys.platform 611 612 if os.name != "posix" or not hasattr(os, 'uname'): 613 # XXX what about the architecture? NT is Intel or Alpha 614 return sys.platform 615 616 # Set for cross builds explicitly 617 if "_PYTHON_HOST_PLATFORM" in os.environ: 618 return os.environ["_PYTHON_HOST_PLATFORM"] 619 620 # Try to distinguish various flavours of Unix 621 osname, host, release, version, machine = os.uname() 622 623 # Convert the OS name to lowercase, remove '/' characters, and translate 624 # spaces (for "Power Macintosh") 625 osname = osname.lower().replace('/', '') 626 machine = machine.replace(' ', '_') 627 machine = machine.replace('/', '-') 628 629 if osname[:5] == "linux": 630 if sys.platform == "android": 631 osname = "android" 632 release = get_config_var("ANDROID_API_LEVEL") 633 634 # Wheel tags use the ABI names from Android's own tools. 635 machine = { 636 "x86_64": "x86_64", 637 "i686": "x86", 638 "aarch64": "arm64_v8a", 639 "armv7l": "armeabi_v7a", 640 }[machine] 641 else: 642 # At least on Linux/Intel, 'machine' is the processor -- 643 # i386, etc. 644 # XXX what about Alpha, SPARC, etc? 645 return f"{osname}-{machine}" 646 elif osname[:5] == "sunos": 647 if release[0] >= "5": # SunOS 5 == Solaris 2 648 osname = "solaris" 649 release = f"{int(release[0]) - 3}.{release[2:]}" 650 # We can't use "platform.architecture()[0]" because a 651 # bootstrap problem. We use a dict to get an error 652 # if some suspicious happens. 653 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} 654 machine += f".{bitness[sys.maxsize]}" 655 # fall through to standard osname-release-machine representation 656 elif osname[:3] == "aix": 657 from _aix_support import aix_platform 658 return aix_platform() 659 elif osname[:6] == "cygwin": 660 osname = "cygwin" 661 import re 662 rel_re = re.compile(r'[\d.]+') 663 m = rel_re.match(release) 664 if m: 665 release = m.group() 666 elif osname[:6] == "darwin": 667 if sys.platform == "ios": 668 release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") 669 osname = sys.platform 670 machine = sys.implementation._multiarch 671 else: 672 import _osx_support 673 osname, release, machine = _osx_support.get_platform_osx( 674 get_config_vars(), 675 osname, release, machine) 676 677 return f"{osname}-{release}-{machine}" 678 679 680def get_python_version(): 681 return _PY_VERSION_SHORT 682 683 684def _get_python_version_abi(): 685 return _PY_VERSION_SHORT + get_config_var("abi_thread") 686 687 688def expand_makefile_vars(s, vars): 689 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 690 'string' according to 'vars' (a dictionary mapping variable names to 691 values). Variables not present in 'vars' are silently expanded to the 692 empty string. The variable values in 'vars' should not contain further 693 variable expansions; if 'vars' is the output of 'parse_makefile()', 694 you're fine. Returns a variable-expanded version of 's'. 695 """ 696 import re 697 698 # This algorithm does multiple expansion, so if vars['foo'] contains 699 # "${bar}", it will expand ${foo} to ${bar}, and then expand 700 # ${bar}... and so forth. This is fine as long as 'vars' comes from 701 # 'parse_makefile()', which takes care of such expansions eagerly, 702 # according to make's variable expansion semantics. 703 704 while True: 705 m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s) 706 if m: 707 (beg, end) = m.span() 708 s = s[0:beg] + vars.get(m.group(1)) + s[end:] 709 else: 710 break 711 return s 712