codekingpro/portable-devtools
114k
1"""Access to Python's configuration information."""2 3import os4import sys5import threading6from os.path import realpath7 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 change64 # the installation layout (eg. different site-packages directory).65 # So, distributors will change the default scheme to one that correctly66 # represents their layout.67 # This presents an issue for projects/people that need to bootstrap virtual68 # environments, like virtualenv. As distributors might now be customizing69 # the default install scheme, there is no guarantee that the information70 # returned by sysconfig.get_default_scheme/get_paths is correct for71 # a virtual environment, the only guarantee we have is that it is correct72 # for the *current* environment. When bootstrapping a virtual environment,73 # we need to know its layout, so that we can place the files in the74 # 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 to78 # leave the following schemes unchanged79 '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_base118 119 # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories.120 # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling.121 system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0]122 if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}:123 return None124 125 def joinuser(*args):126 return os.path.expanduser(os.path.join(*args))127 128 if os.name == "nt":129 base = os.environ.get("APPDATA") or "~"130 return joinuser(base, _get_implementation())131 132 if sys.platform == "darwin" and sys._framework:133 return joinuser("~", "Library", sys._framework,134 f"{sys.version_info[0]}.{sys.version_info[1]}")135 136 return joinuser("~", ".local")137 138_HAS_USER_BASE = (_getuserbase() is not None)139 140if _HAS_USER_BASE:141 _INSTALL_SCHEMES |= {142 # NOTE: When modifying "purelib" scheme, update site._get_path() too.143 'nt_user': {144 'stdlib': '{userbase}/{implementation}{py_version_nodot_plat}',145 'platstdlib': '{userbase}/{implementation}{py_version_nodot_plat}',146 'purelib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages',147 'platlib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages',148 'include': '{userbase}/{implementation}{py_version_nodot_plat}/Include',149 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts',150 'data': '{userbase}',151 },152 'posix_user': {153 'stdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}',154 'platstdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}',155 'purelib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages',156 'platlib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages',157 'include': '{userbase}/include/{implementation_lower}{py_version_short}{abi_thread}',158 'scripts': '{userbase}/bin',159 'data': '{userbase}',160 },161 'osx_framework_user': {162 'stdlib': '{userbase}/lib/{implementation_lower}',163 'platstdlib': '{userbase}/lib/{implementation_lower}',164 'purelib': '{userbase}/lib/{implementation_lower}/site-packages',165 'platlib': '{userbase}/lib/{implementation_lower}/site-packages',166 'include': '{userbase}/include/{implementation_lower}{py_version_short}',167 'scripts': '{userbase}/bin',168 'data': '{userbase}',169 },170 }171 172_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',173 'scripts', 'data')174 175_PY_VERSION = sys.version.split()[0]176_PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'177_PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'178_PREFIX = os.path.normpath(sys.prefix)179_BASE_PREFIX = os.path.normpath(sys.base_prefix)180_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)181# Mutex guarding initialization of _CONFIG_VARS.182_CONFIG_VARS_LOCK = threading.RLock()183_CONFIG_VARS = None184# True iff _CONFIG_VARS has been fully initialized.185_CONFIG_VARS_INITIALIZED = False186_USER_BASE = None187 188 189def _safe_realpath(path):190 try:191 return realpath(path)192 except OSError:193 return path194 195if sys.executable:196 _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))197else:198 # sys.executable can be empty if argv[0] has been changed and Python is199 # unable to retrieve the real program name200 _PROJECT_BASE = _safe_realpath(os.getcwd())201 202# In a virtual environment, `sys._home` gives us the target directory203# `_PROJECT_BASE` for the executable that created it when the virtual204# python is an actual executable ('venv --copies' or Windows).205_sys_home = getattr(sys, '_home', None)206if _sys_home:207 _PROJECT_BASE = _sys_home208 209if os.name == 'nt':210 # In a source build, the executable is in a subdirectory of the root211 # that we want (<root>\PCbuild\<platname>).212 # `_BASE_PREFIX` is used as the base installation is where the source213 # will be. The realpath is needed to prevent mount point confusion214 # that can occur with just string comparisons.215 if _safe_realpath(_PROJECT_BASE).startswith(216 _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')):217 _PROJECT_BASE = _BASE_PREFIX218 219# set for cross builds220if "_PYTHON_PROJECT_BASE" in os.environ:221 _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"])222 223def is_python_build(check_home=None):224 if check_home is not None:225 import warnings226 warnings.warn(227 (228 'The check_home argument of sysconfig.is_python_build is '229 'deprecated and its value is ignored. '230 'It will be removed in Python 3.15.'231 ),232 DeprecationWarning,233 stacklevel=2,234 )235 for fn in ("Setup", "Setup.local"):236 if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):237 return True238 return False239 240_PYTHON_BUILD = is_python_build()241 242if _PYTHON_BUILD:243 for scheme in ('posix_prefix', 'posix_home'):244 # On POSIX-y platforms, Python will:245 # - Build from .h files in 'headers' (which is only added to the246 # scheme when building CPython)247 # - Install .h files to 'include'248 scheme = _INSTALL_SCHEMES[scheme]249 scheme['headers'] = scheme['include']250 scheme['include'] = '{srcdir}/Include'251 scheme['platinclude'] = '{projectbase}/.'252 del scheme253 254 255def _subst_vars(s, local_vars):256 try:257 return s.format(**local_vars)258 except KeyError as var:259 try:260 return s.format(**os.environ)261 except KeyError:262 raise AttributeError(f'{var}') from None263 264def _extend_dict(target_dict, other_dict):265 target_keys = target_dict.keys()266 for key, value in other_dict.items():267 if key in target_keys:268 continue269 target_dict[key] = value270 271 272def _expand_vars(scheme, vars):273 res = {}274 if vars is None:275 vars = {}276 _extend_dict(vars, get_config_vars())277 if os.name == 'nt':278 # On Windows we want to substitute 'lib' for schemes rather279 # than the native value (without modifying vars, in case it280 # was passed in)281 vars = vars | {'platlibdir': 'lib'}282 283 for key, value in _INSTALL_SCHEMES[scheme].items():284 if os.name in ('posix', 'nt'):285 value = os.path.expanduser(value)286 res[key] = os.path.normpath(_subst_vars(value, vars))287 return res288 289 290def _get_preferred_schemes():291 if os.name == 'nt':292 return {293 'prefix': 'nt',294 'home': 'posix_home',295 'user': 'nt_user',296 }297 if sys.platform == 'darwin' and sys._framework:298 return {299 'prefix': 'posix_prefix',300 'home': 'posix_home',301 'user': 'osx_framework_user',302 }303 304 return {305 'prefix': 'posix_prefix',306 'home': 'posix_home',307 'user': 'posix_user',308 }309 310 311def get_preferred_scheme(key):312 if key == 'prefix' and sys.prefix != sys.base_prefix:313 return 'venv'314 scheme = _get_preferred_schemes()[key]315 if scheme not in _INSTALL_SCHEMES:316 raise ValueError(317 f"{key!r} returned {scheme!r}, which is not a valid scheme "318 f"on this platform"319 )320 return scheme321 322 323def get_default_scheme():324 return get_preferred_scheme('prefix')325 326 327def get_makefile_filename():328 """Return the path of the Makefile."""329 330 # GH-127429: When cross-compiling, use the Makefile from the target, instead of the host Python.331 if cross_base := os.environ.get('_PYTHON_PROJECT_BASE'):332 return os.path.join(cross_base, 'Makefile')333 334 if _PYTHON_BUILD:335 return os.path.join(_PROJECT_BASE, "Makefile")336 337 if hasattr(sys, 'abiflags'):338 config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}'339 else:340 config_dir_name = 'config'341 342 if hasattr(sys.implementation, '_multiarch'):343 config_dir_name += f'-{sys.implementation._multiarch}'344 345 return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')346 347 348def _import_from_directory(path, name):349 if name not in sys.modules:350 import importlib.machinery351 import importlib.util352 353 spec = importlib.machinery.PathFinder.find_spec(name, [path])354 module = importlib.util.module_from_spec(spec)355 spec.loader.exec_module(module)356 sys.modules[name] = module357 return sys.modules[name]358 359 360def _get_sysconfigdata_name():361 multiarch = getattr(sys.implementation, '_multiarch', '')362 return os.environ.get(363 '_PYTHON_SYSCONFIGDATA_NAME',364 f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',365 )366 367 368def _get_sysconfigdata():369 import importlib370 371 name = _get_sysconfigdata_name()372 path = os.environ.get('_PYTHON_SYSCONFIGDATA_PATH')373 module = _import_from_directory(path, name) if path else importlib.import_module(name)374 375 return module.build_time_vars376 377 378def _installation_is_relocated():379 """Is the Python installation running from a different prefix than what was targetted when building?"""380 if os.name != 'posix':381 raise NotImplementedError('sysconfig._installation_is_relocated() is currently only supported on POSIX')382 383 data = _get_sysconfigdata()384 return (385 data['prefix'] != getattr(sys, 'base_prefix', '')386 or data['exec_prefix'] != getattr(sys, 'base_exec_prefix', '')387 )388 389 390def _init_posix(vars):391 """Initialize the module as appropriate for POSIX systems."""392 # GH-126920: Make sure we don't overwrite any of the keys already set393 vars.update(_get_sysconfigdata() | vars)394 395 396def _init_non_posix(vars):397 """Initialize the module as appropriate for NT"""398 # set basic install directories399 import _winapi400 import _sysconfig401 vars['LIBDEST'] = get_path('stdlib')402 vars['BINLIBDEST'] = get_path('platstdlib')403 vars['INCLUDEPY'] = get_path('include')404 405 # Add EXT_SUFFIX, SOABI, Py_DEBUG, and Py_GIL_DISABLED406 vars.update(_sysconfig.config_vars())407 408 # NOTE: ABIFLAGS is only an emulated value. It is not present during build409 # on Windows. sys.abiflags is absent on Windows and vars['abiflags']410 # is already widely used to calculate paths, so it should remain an411 # empty string.412 vars['ABIFLAGS'] = ''.join(413 (414 't' if vars['Py_GIL_DISABLED'] else '',415 '_d' if vars['Py_DEBUG'] else '',416 ),417 )418 419 vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs'))420 if hasattr(sys, 'dllhandle'):421 dllhandle = _winapi.GetModuleFileName(sys.dllhandle)422 vars['LIBRARY'] = os.path.basename(_safe_realpath(dllhandle))423 vars['LDLIBRARY'] = vars['LIBRARY']424 vars['EXE'] = '.exe'425 vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT426 vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))427 vars['TZPATH'] = ''428 429#430# public APIs431#432 433 434def parse_config_h(fp, vars=None):435 """Parse a config.h-style file.436 437 A dictionary containing name/value pairs is returned. If an438 optional dictionary is passed in as the second argument, it is439 used instead of a new dictionary.440 """441 if vars is None:442 vars = {}443 import re444 define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")445 undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")446 447 while True:448 line = fp.readline()449 if not line:450 break451 m = define_rx.match(line)452 if m:453 n, v = m.group(1, 2)454 try:455 if n in _ALWAYS_STR:456 raise ValueError457 v = int(v)458 except ValueError:459 pass460 vars[n] = v461 else:462 m = undef_rx.match(line)463 if m:464 vars[m.group(1)] = 0465 return vars466 467 468def get_config_h_filename():469 """Return the path of pyconfig.h."""470 if _PYTHON_BUILD:471 if os.name == "nt":472 inc_dir = os.path.join(_PROJECT_BASE, 'PC')473 else:474 inc_dir = _PROJECT_BASE475 else:476 inc_dir = get_path('platinclude')477 return os.path.join(inc_dir, 'pyconfig.h')478 479 480def get_scheme_names():481 """Return a tuple containing the schemes names."""482 return tuple(sorted(_INSTALL_SCHEMES))483 484 485def get_path_names():486 """Return a tuple containing the paths names."""487 return _SCHEME_KEYS488 489 490def get_paths(scheme=get_default_scheme(), vars=None, expand=True):491 """Return a mapping containing an install scheme.492 493 ``scheme`` is the install scheme name. If not provided, it will494 return the default scheme for the current platform.495 """496 if expand:497 return _expand_vars(scheme, vars)498 else:499 return _INSTALL_SCHEMES[scheme]500 501 502def get_path(name, scheme=get_default_scheme(), vars=None, expand=True):503 """Return a path corresponding to the scheme.504 505 ``scheme`` is the install scheme name.506 """507 return get_paths(scheme, vars, expand)[name]508 509 510def _init_config_vars():511 global _CONFIG_VARS512 _CONFIG_VARS = {}513 514 prefix = os.path.normpath(sys.prefix)515 exec_prefix = os.path.normpath(sys.exec_prefix)516 base_prefix = _BASE_PREFIX517 base_exec_prefix = _BASE_EXEC_PREFIX518 519 try:520 abiflags = sys.abiflags521 except AttributeError:522 abiflags = ''523 524 if os.name == 'posix':525 _init_posix(_CONFIG_VARS)526 # If we are cross-compiling, load the prefixes from the Makefile instead.527 if '_PYTHON_PROJECT_BASE' in os.environ:528 prefix = _CONFIG_VARS['host_prefix']529 exec_prefix = _CONFIG_VARS['host_exec_prefix']530 base_prefix = _CONFIG_VARS['host_prefix']531 base_exec_prefix = _CONFIG_VARS['host_exec_prefix']532 abiflags = _CONFIG_VARS['ABIFLAGS']533 534 # Normalized versions of prefix and exec_prefix are handy to have;535 # in fact, these are the standard versions used most places in the536 # Distutils.537 _CONFIG_VARS['prefix'] = prefix538 _CONFIG_VARS['exec_prefix'] = exec_prefix539 _CONFIG_VARS['py_version'] = _PY_VERSION540 _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT541 _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT542 _CONFIG_VARS['installed_base'] = base_prefix543 _CONFIG_VARS['base'] = prefix544 _CONFIG_VARS['installed_platbase'] = base_exec_prefix545 _CONFIG_VARS['platbase'] = exec_prefix546 _CONFIG_VARS['projectbase'] = _PROJECT_BASE547 _CONFIG_VARS['platlibdir'] = sys.platlibdir548 _CONFIG_VARS['implementation'] = _get_implementation()549 _CONFIG_VARS['implementation_lower'] = _get_implementation().lower()550 _CONFIG_VARS['abiflags'] = abiflags551 try:552 _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '')553 except AttributeError:554 _CONFIG_VARS['py_version_nodot_plat'] = ''555 556 if os.name == 'nt':557 _init_non_posix(_CONFIG_VARS)558 _CONFIG_VARS['VPATH'] = sys._vpath559 _CONFIG_VARS['TZPATH'] = os.path.join(_PREFIX, "share", "zoneinfo")560 if _HAS_USER_BASE:561 # Setting 'userbase' is done below the call to the562 # init function to enable using 'get_config_var' in563 # the init-function.564 _CONFIG_VARS['userbase'] = _getuserbase()565 566 # e.g., 't' for free-threaded or '' for default build567 _CONFIG_VARS['abi_thread'] = 't' if _CONFIG_VARS.get('Py_GIL_DISABLED') else ''568 569 # Always convert srcdir to an absolute path570 srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE)571 if os.name == 'posix':572 if _PYTHON_BUILD:573 # If srcdir is a relative path (typically '.' or '..')574 # then it should be interpreted relative to the directory575 # containing Makefile.576 base = os.path.dirname(get_makefile_filename())577 srcdir = os.path.join(base, srcdir)578 else:579 # srcdir is not meaningful since the installation is580 # spread about the filesystem. We choose the581 # directory containing the Makefile since we know it582 # exists.583 srcdir = os.path.dirname(get_makefile_filename())584 _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir)585 586 # OS X platforms require special customization to handle587 # multi-architecture, multi-os-version installers588 if sys.platform == 'darwin':589 import _osx_support590 _osx_support.customize_config_vars(_CONFIG_VARS)591 592 global _CONFIG_VARS_INITIALIZED593 _CONFIG_VARS_INITIALIZED = True594 595 596def get_config_vars(*args):597 """With no arguments, return a dictionary of all configuration598 variables relevant for the current platform.599 600 On Unix, this means every variable defined in Python's installed Makefile;601 On Windows it's a much smaller set.602 603 With arguments, return a list of values that result from looking up604 each argument in the configuration variable dictionary.605 """606 global _CONFIG_VARS_INITIALIZED607 608 # Avoid claiming the lock once initialization is complete.609 if _CONFIG_VARS_INITIALIZED:610 # GH-126789: If sys.prefix or sys.exec_prefix were updated, invalidate the cache.611 prefix = os.path.normpath(sys.prefix)612 exec_prefix = os.path.normpath(sys.exec_prefix)613 if _CONFIG_VARS['prefix'] != prefix or _CONFIG_VARS['exec_prefix'] != exec_prefix:614 with _CONFIG_VARS_LOCK:615 _CONFIG_VARS_INITIALIZED = False616 _init_config_vars()617 else:618 # Initialize the config_vars cache.619 with _CONFIG_VARS_LOCK:620 # Test again with the lock held to avoid races. Note that621 # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED,622 # to ensure that recursive calls to get_config_vars()623 # don't re-enter init_config_vars().624 if _CONFIG_VARS is None:625 _init_config_vars()626 627 if args:628 vals = []629 for name in args:630 vals.append(_CONFIG_VARS.get(name))631 return vals632 else:633 return _CONFIG_VARS634 635 636def get_config_var(name):637 """Return the value of a single variable using the dictionary returned by638 'get_config_vars()'.639 640 Equivalent to get_config_vars().get(name)641 """642 return get_config_vars().get(name)643 644 645def get_platform():646 """Return a string that identifies the current platform.647 648 This is used mainly to distinguish platform-specific build directories and649 platform-specific built distributions. Typically includes the OS name and650 version and the architecture (as supplied by 'os.uname()'), although the651 exact information included depends on the OS; on Linux, the kernel version652 isn't particularly important.653 654 Examples of returned values:655 656 657 Windows:658 659 - win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T)660 - win-arm64 (64-bit Windows on ARM64, aka AArch64)661 - win32 (all others - specifically, sys.platform is returned)662 663 POSIX based OS:664 665 - linux-x86_64666 - macosx-15.5-arm64667 - macosx-26.0-universal2 (macOS on Apple Silicon or Intel)668 - android-24-arm64_v8a669 670 For other non-POSIX platforms, currently just returns :data:`sys.platform`."""671 if os.name == 'nt':672 if 'amd64' in sys.version.lower():673 return 'win-amd64'674 if '(arm)' in sys.version.lower():675 return 'win-arm32'676 if '(arm64)' in sys.version.lower():677 return 'win-arm64'678 return sys.platform679 680 if os.name != "posix" or not hasattr(os, 'uname'):681 # XXX what about the architecture? NT is Intel or Alpha682 return sys.platform683 684 # Set for cross builds explicitly685 if "_PYTHON_HOST_PLATFORM" in os.environ:686 osname, _, machine = os.environ["_PYTHON_HOST_PLATFORM"].partition('-')687 release = None688 else:689 # Try to distinguish various flavours of Unix690 osname, host, release, version, machine = os.uname()691 692 # Convert the OS name to lowercase, remove '/' characters, and translate693 # spaces (for "Power Macintosh")694 osname = osname.lower().replace('/', '')695 machine = machine.replace(' ', '_')696 machine = machine.replace('/', '-')697 698 if osname == "android" or sys.platform == "android":699 osname = "android"700 release = get_config_var("ANDROID_API_LEVEL")701 702 # Wheel tags use the ABI names from Android's own tools.703 # When Python is running on 32-bit ARM Android on a 64-bit ARM kernel,704 # 'os.uname().machine' is 'armv8l'. Such devices run the same userspace705 # code as 'armv7l' devices.706 # During the build process of the Android testbed when targeting 32-bit ARM,707 # '_PYTHON_HOST_PLATFORM' is 'arm-linux-androideabi', so 'machine' becomes708 # 'arm'.709 machine = {710 "aarch64": "arm64_v8a",711 "arm": "armeabi_v7a",712 "armv7l": "armeabi_v7a",713 "armv8l": "armeabi_v7a",714 "i686": "x86",715 "x86_64": "x86_64",716 }[machine]717 elif osname == "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 2724 osname = "solaris"725 release = f"{int(release[0]) - 3}.{release[2:]}"726 # We can't use "platform.architecture()[0]" because a727 # bootstrap problem. We use a dict to get an error728 # if some suspicious happens.729 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}730 machine += f".{bitness[sys.maxsize]}"731 # fall through to standard osname-release-machine representation732 elif osname[:3] == "aix":733 from _aix_support import aix_platform734 return aix_platform()735 elif osname[:6] == "cygwin":736 osname = "cygwin"737 import re738 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 if sys.platform == "ios":744 release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0")745 osname = sys.platform746 machine = sys.implementation._multiarch747 else:748 import _osx_support749 osname, release, machine = _osx_support.get_platform_osx(750 get_config_vars(),751 osname, release, machine)752 753 return '-'.join(map(str, filter(None, (osname, release, machine))))754 755 756def get_python_version():757 return _PY_VERSION_SHORT758 759 760def _get_python_version_abi():761 return _PY_VERSION_SHORT + get_config_var("abi_thread")762 763 764def expand_makefile_vars(s, vars):765 """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in766 'string' according to 'vars' (a dictionary mapping variable names to767 values). Variables not present in 'vars' are silently expanded to the768 empty string. The variable values in 'vars' should not contain further769 variable expansions; if 'vars' is the output of 'parse_makefile()',770 you're fine. Returns a variable-expanded version of 's'.771 """772 773 import warnings774 warnings.warn(775 'sysconfig.expand_makefile_vars is deprecated and will be removed in '776 'Python 3.16. Use sysconfig.get_paths(vars=...) instead.',777 DeprecationWarning,778 stacklevel=2,779 )780 781 import re782 783 _findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"784 _findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"785 786 # This algorithm does multiple expansion, so if vars['foo'] contains787 # "${bar}", it will expand ${foo} to ${bar}, and then expand788 # ${bar}... and so forth. This is fine as long as 'vars' comes from789 # 'parse_makefile()', which takes care of such expansions eagerly,790 # according to make's variable expansion semantics.791 792 while True:793 m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s)794 if m:795 (beg, end) = m.span()796 s = s[0:beg] + vars.get(m.group(1)) + s[end:]797 else:798 break799 return s800 