| """Access to Python's configuration information.""" | |
| import os | |
| import sys | |
| import threading | |
| from os.path import realpath | |
| __all__ = [ | |
| 'get_config_h_filename', | |
| 'get_config_var', | |
| 'get_config_vars', | |
| 'get_makefile_filename', | |
| 'get_path', | |
| 'get_path_names', | |
| 'get_paths', | |
| 'get_platform', | |
| 'get_python_version', | |
| 'get_scheme_names', | |
| 'parse_config_h', | |
| ] | |
| # Keys for get_config_var() that are never converted to Python integers. | |
| _ALWAYS_STR = { | |
| 'IPHONEOS_DEPLOYMENT_TARGET', | |
| 'MACOSX_DEPLOYMENT_TARGET', | |
| } | |
| _INSTALL_SCHEMES = { | |
| 'posix_prefix': { | |
| 'stdlib': '{installed_base}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'platstdlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'purelib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'include': | |
| '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', | |
| 'platinclude': | |
| '{installed_platbase}/include/{implementation_lower}{py_version_short}{abiflags}', | |
| 'scripts': '{base}/bin', | |
| 'data': '{base}', | |
| }, | |
| 'posix_home': { | |
| 'stdlib': '{installed_base}/lib/{implementation_lower}', | |
| 'platstdlib': '{base}/lib/{implementation_lower}', | |
| 'purelib': '{base}/lib/{implementation_lower}', | |
| 'platlib': '{base}/lib/{implementation_lower}', | |
| 'include': '{installed_base}/include/{implementation_lower}', | |
| 'platinclude': '{installed_base}/include/{implementation_lower}', | |
| 'scripts': '{base}/bin', | |
| 'data': '{base}', | |
| }, | |
| 'nt': { | |
| 'stdlib': '{installed_base}/Lib', | |
| 'platstdlib': '{base}/Lib', | |
| 'purelib': '{base}/Lib/site-packages', | |
| 'platlib': '{base}/Lib/site-packages', | |
| 'include': '{installed_base}/Include', | |
| 'platinclude': '{installed_base}/Include', | |
| 'scripts': '{base}/Scripts', | |
| 'data': '{base}', | |
| }, | |
| # Downstream distributors can overwrite the default install scheme. | |
| # This is done to support downstream modifications where distributors change | |
| # the installation layout (eg. different site-packages directory). | |
| # So, distributors will change the default scheme to one that correctly | |
| # represents their layout. | |
| # This presents an issue for projects/people that need to bootstrap virtual | |
| # environments, like virtualenv. As distributors might now be customizing | |
| # the default install scheme, there is no guarantee that the information | |
| # returned by sysconfig.get_default_scheme/get_paths is correct for | |
| # a virtual environment, the only guarantee we have is that it is correct | |
| # for the *current* environment. When bootstrapping a virtual environment, | |
| # we need to know its layout, so that we can place the files in the | |
| # correct locations. | |
| # The "*_venv" install scheme is a scheme to bootstrap virtual environments, | |
| # essentially identical to the default posix_prefix/nt schemes. | |
| # Downstream distributors who patch posix_prefix/nt scheme are encouraged to | |
| # leave the following schemes unchanged | |
| 'posix_venv': { | |
| 'stdlib': '{installed_base}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'platstdlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'purelib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'include': | |
| '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', | |
| 'platinclude': | |
| '{installed_platbase}/include/{implementation_lower}{py_version_short}{abiflags}', | |
| 'scripts': '{base}/bin', | |
| 'data': '{base}', | |
| }, | |
| 'nt_venv': { | |
| 'stdlib': '{installed_base}/Lib', | |
| 'platstdlib': '{base}/Lib', | |
| 'purelib': '{base}/Lib/site-packages', | |
| 'platlib': '{base}/Lib/site-packages', | |
| 'include': '{installed_base}/Include', | |
| 'platinclude': '{installed_base}/Include', | |
| 'scripts': '{base}/Scripts', | |
| 'data': '{base}', | |
| }, | |
| } | |
| # For the OS-native venv scheme, we essentially provide an alias: | |
| if os.name == 'nt': | |
| _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv'] | |
| else: | |
| _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] | |
| def _get_implementation(): | |
| return 'Python' | |
| # NOTE: site.py has copy of this function. | |
| # Sync it when modify this function. | |
| def _getuserbase(): | |
| env_base = os.environ.get("PYTHONUSERBASE", None) | |
| if env_base: | |
| return env_base | |
| # Emscripten, iOS, tvOS, VxWorks, WASI, and watchOS have no home directories. | |
| # Use _PYTHON_HOST_PLATFORM to get the correct platform when cross-compiling. | |
| system_name = os.environ.get('_PYTHON_HOST_PLATFORM', sys.platform).split('-')[0] | |
| if system_name in {"emscripten", "ios", "tvos", "vxworks", "wasi", "watchos"}: | |
| return None | |
| def joinuser(*args): | |
| return os.path.expanduser(os.path.join(*args)) | |
| if os.name == "nt": | |
| base = os.environ.get("APPDATA") or "~" | |
| return joinuser(base, _get_implementation()) | |
| if sys.platform == "darwin" and sys._framework: | |
| return joinuser("~", "Library", sys._framework, | |
| f"{sys.version_info[0]}.{sys.version_info[1]}") | |
| return joinuser("~", ".local") | |
| _HAS_USER_BASE = (_getuserbase() is not None) | |
| if _HAS_USER_BASE: | |
| _INSTALL_SCHEMES |= { | |
| # NOTE: When modifying "purelib" scheme, update site._get_path() too. | |
| 'nt_user': { | |
| 'stdlib': '{userbase}/{implementation}{py_version_nodot_plat}', | |
| 'platstdlib': '{userbase}/{implementation}{py_version_nodot_plat}', | |
| 'purelib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', | |
| 'platlib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', | |
| 'include': '{userbase}/{implementation}{py_version_nodot_plat}/Include', | |
| 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts', | |
| 'data': '{userbase}', | |
| }, | |
| 'posix_user': { | |
| 'stdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'platstdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'purelib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'platlib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', | |
| 'include': '{userbase}/include/{implementation_lower}{py_version_short}{abi_thread}', | |
| 'scripts': '{userbase}/bin', | |
| 'data': '{userbase}', | |
| }, | |
| 'osx_framework_user': { | |
| 'stdlib': '{userbase}/lib/{implementation_lower}', | |
| 'platstdlib': '{userbase}/lib/{implementation_lower}', | |
| 'purelib': '{userbase}/lib/{implementation_lower}/site-packages', | |
| 'platlib': '{userbase}/lib/{implementation_lower}/site-packages', | |
| 'include': '{userbase}/include/{implementation_lower}{py_version_short}', | |
| 'scripts': '{userbase}/bin', | |
| 'data': '{userbase}', | |
| }, | |
| } | |
| _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', | |
| 'scripts', 'data') | |
| _PY_VERSION = sys.version.split()[0] | |
| _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}' | |
| _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}' | |
| _BASE_PREFIX = os.path.normpath(sys.base_prefix) | |
| _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) | |
| # Mutex guarding initialization of _CONFIG_VARS. | |
| _CONFIG_VARS_LOCK = threading.RLock() | |
| _CONFIG_VARS = None | |
| # True iff _CONFIG_VARS has been fully initialized. | |
| _CONFIG_VARS_INITIALIZED = False | |
| _config_vars_cached_prefix = None | |
| _config_vars_cached_exec_prefix = None | |
| _USER_BASE = None | |
| def _safe_realpath(path): | |
| try: | |
| return realpath(path) | |
| except OSError: | |
| return path | |
| if sys.executable: | |
| _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) | |
| else: | |
| # sys.executable can be empty if argv[0] has been changed and Python is | |
| # unable to retrieve the real program name | |
| _PROJECT_BASE = _safe_realpath(os.getcwd()) | |
| # In a virtual environment, `sys._home` gives us the target directory | |
| # `_PROJECT_BASE` for the executable that created it when the virtual | |
| # python is an actual executable ('venv --copies' or Windows). | |
| _sys_home = getattr(sys, '_home', None) | |
| if _sys_home: | |
| _PROJECT_BASE = _sys_home | |
| if os.name == 'nt': | |
| # In a source build, the executable is in a subdirectory of the root | |
| # that we want (<root>\PCbuild\<platname>). | |
| # `_BASE_PREFIX` is used as the base installation is where the source | |
| # will be. The realpath is needed to prevent mount point confusion | |
| # that can occur with just string comparisons. | |
| if _safe_realpath(_PROJECT_BASE).startswith( | |
| _safe_realpath(f'{_BASE_PREFIX}\\PCbuild')): | |
| _PROJECT_BASE = _BASE_PREFIX | |
| # set for cross builds | |
| if "_PYTHON_PROJECT_BASE" in os.environ: | |
| _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) | |
| def is_python_build(): | |
| for fn in ("Setup", "Setup.local"): | |
| if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): | |
| return True | |
| return False | |
| _PYTHON_BUILD = is_python_build() | |
| if _PYTHON_BUILD: | |
| for scheme in ('posix_prefix', 'posix_home'): | |
| # On POSIX-y platforms, Python will: | |
| # - Build from .h files in 'headers' (which is only added to the | |
| # scheme when building CPython) | |
| # - Install .h files to 'include' | |
| scheme = _INSTALL_SCHEMES[scheme] | |
| scheme['headers'] = scheme['include'] | |
| scheme['include'] = '{srcdir}/Include' | |
| scheme['platinclude'] = '{projectbase}/.' | |
| del scheme | |
| def _subst_vars(s, local_vars): | |
| try: | |
| return s.format(**local_vars) | |
| except KeyError as var: | |
| try: | |
| return s.format(**os.environ) | |
| except KeyError: | |
| raise AttributeError(f'{var}') from None | |
| def _extend_dict(target_dict, other_dict): | |
| target_keys = target_dict.keys() | |
| for key, value in other_dict.items(): | |
| if key in target_keys: | |
| continue | |
| target_dict[key] = value | |
| def _expand_vars(scheme, vars): | |
| res = {} | |
| if vars is None: | |
| vars = {} | |
| _extend_dict(vars, get_config_vars()) | |
| if os.name == 'nt': | |
| # On Windows we want to substitute 'lib' for schemes rather | |
| # than the native value (without modifying vars, in case it | |
| # was passed in) | |
| vars = vars | {'platlibdir': 'lib'} | |
| for key, value in _INSTALL_SCHEMES[scheme].items(): | |
| if os.name in ('posix', 'nt'): | |
| value = os.path.expanduser(value) | |
| res[key] = os.path.normpath(_subst_vars(value, vars)) | |
| return res | |
| def _get_preferred_schemes(): | |
| if os.name == 'nt': | |
| return { | |
| 'prefix': 'nt', | |
| 'home': 'posix_home', | |
| 'user': 'nt_user', | |
| } | |
| if sys.platform == 'darwin' and sys._framework: | |
| return { | |
| 'prefix': 'posix_prefix', | |
| 'home': 'posix_home', | |
| 'user': 'osx_framework_user', | |
| } | |
| return { | |
| 'prefix': 'posix_prefix', | |
| 'home': 'posix_home', | |
| 'user': 'posix_user', | |
| } | |
| def get_preferred_scheme(key): | |
| if key == 'prefix' and sys.prefix != sys.base_prefix: | |
| return 'venv' | |
| scheme = _get_preferred_schemes()[key] | |
| if scheme not in _INSTALL_SCHEMES: | |
| raise ValueError( | |
| f"{key!r} returned {scheme!r}, which is not a valid scheme " | |
| f"on this platform" | |
| ) | |
| return scheme | |
| def get_default_scheme(): | |
| return get_preferred_scheme('prefix') | |
| def get_makefile_filename(): | |
| """Return the path of the Makefile.""" | |
| # GH-127429: When cross-compiling, use the Makefile from the target, instead of the host Python. | |
| if cross_base := os.environ.get('_PYTHON_PROJECT_BASE'): | |
| return os.path.join(cross_base, 'Makefile') | |
| if _PYTHON_BUILD: | |
| return os.path.join(_PROJECT_BASE, "Makefile") | |
| if hasattr(sys, 'abiflags'): | |
| config_dir_name = f'config-{_PY_VERSION_SHORT}{sys.abiflags}' | |
| else: | |
| config_dir_name = 'config' | |
| if hasattr(sys.implementation, '_multiarch'): | |
| config_dir_name += f'-{sys.implementation._multiarch}' | |
| return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') | |
| def _import_from_directory(path, name): | |
| if name not in sys.modules: | |
| import importlib.machinery | |
| import importlib.util | |
| spec = importlib.machinery.PathFinder.find_spec(name, [path]) | |
| module = importlib.util.module_from_spec(spec) | |
| spec.loader.exec_module(module) | |
| sys.modules[name] = module | |
| return sys.modules[name] | |
| def _get_sysconfigdata_name(): | |
| multiarch = getattr(sys.implementation, '_multiarch', '') | |
| return os.environ.get( | |
| '_PYTHON_SYSCONFIGDATA_NAME', | |
| f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}', | |
| ) | |
| def _get_sysconfigdata(): | |
| import importlib | |
| name = _get_sysconfigdata_name() | |
| path = os.environ.get('_PYTHON_SYSCONFIGDATA_PATH') | |
| module = _import_from_directory(path, name) if path else importlib.import_module(name) | |
| return module.build_time_vars | |
| def _installation_is_relocated(): | |
| """Is the Python installation running from a different prefix than what was targetted when building?""" | |
| if os.name != 'posix': | |
| raise NotImplementedError('sysconfig._installation_is_relocated() is currently only supported on POSIX') | |
| data = _get_sysconfigdata() | |
| return ( | |
| data['prefix'] != getattr(sys, 'base_prefix', '') | |
| or data['exec_prefix'] != getattr(sys, 'base_exec_prefix', '') | |
| ) | |
| def _init_posix(vars): | |
| """Initialize the module as appropriate for POSIX systems.""" | |
| # GH-126920: Make sure we don't overwrite any of the keys already set | |
| vars.update(_get_sysconfigdata() | vars) | |
| def _init_non_posix(vars): | |
| """Initialize the module as appropriate for NT""" | |
| # set basic install directories | |
| import _winapi | |
| import _sysconfig | |
| vars['LIBDEST'] = get_path('stdlib') | |
| vars['BINLIBDEST'] = get_path('platstdlib') | |
| vars['INCLUDEPY'] = get_path('include') | |
| # Add EXT_SUFFIX, SOABI, Py_DEBUG, and Py_GIL_DISABLED | |
| vars.update(_sysconfig.config_vars()) | |
| # NOTE: ABIFLAGS is only an emulated value. It is not present during build | |
| # on Windows. sys.abiflags is absent on Windows and vars['abiflags'] | |
| # is already widely used to calculate paths, so it should remain an | |
| # empty string. | |
| vars['ABIFLAGS'] = ''.join( | |
| ( | |
| 't' if vars['Py_GIL_DISABLED'] else '', | |
| '_d' if vars['Py_DEBUG'] else '', | |
| ), | |
| ) | |
| vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs')) | |
| if hasattr(sys, 'dllhandle'): | |
| dllhandle = _winapi.GetModuleFileName(sys.dllhandle) | |
| vars['LIBRARY'] = os.path.basename(_safe_realpath(dllhandle)) | |
| vars['LDLIBRARY'] = vars['LIBRARY'] | |
| vars['EXE'] = '.exe' | |
| vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT | |
| vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) | |
| # No standard path exists on Windows for this, but we'll check | |
| # whether someone is imitating a POSIX-like layout | |
| check_tzpath = os.path.join(vars['prefix'], 'share', 'zoneinfo') | |
| if os.path.exists(check_tzpath): | |
| vars['TZPATH'] = check_tzpath | |
| else: | |
| vars['TZPATH'] = '' | |
| # | |
| # public APIs | |
| # | |
| def parse_config_h(fp, vars=None): | |
| """Parse a config.h-style file. | |
| A dictionary containing name/value pairs is returned. If an | |
| optional dictionary is passed in as the second argument, it is | |
| used instead of a new dictionary. | |
| """ | |
| if vars is None: | |
| vars = {} | |
| import re | |
| name_rx = '(?:[A-Z]|_Py_)[A-Za-z0-9_]+' | |
| define_rx = re.compile(fr"#define ({name_rx}) (.*)\n") | |
| undef_rx = re.compile(fr"/[*] #undef ({name_rx}) [*]/\n") | |
| quoted_re = re.compile(r'"(.*)"') | |
| while True: | |
| line = fp.readline() | |
| if not line: | |
| break | |
| m = define_rx.match(line) | |
| if m: | |
| n, v = m.group(1, 2) | |
| if mq := quoted_re.fullmatch(v): | |
| v = mq.group(1) | |
| try: | |
| if n in _ALWAYS_STR: | |
| raise ValueError | |
| v = int(v) | |
| except ValueError: | |
| pass | |
| vars[n] = v | |
| else: | |
| m = undef_rx.match(line) | |
| if m: | |
| vars[m.group(1)] = 0 | |
| return vars | |
| def get_config_h_filename(): | |
| """Return the path of pyconfig.h.""" | |
| if _PYTHON_BUILD: | |
| if os.name == "nt": | |
| inc_dir = os.path.join(_PROJECT_BASE, 'PC') | |
| else: | |
| inc_dir = _PROJECT_BASE | |
| else: | |
| inc_dir = get_path('platinclude') | |
| return os.path.join(inc_dir, 'pyconfig.h') | |
| def get_scheme_names(): | |
| """Return a tuple containing the schemes names.""" | |
| return tuple(sorted(_INSTALL_SCHEMES)) | |
| def get_path_names(): | |
| """Return a tuple containing the paths names.""" | |
| return _SCHEME_KEYS | |
| def get_paths(scheme=get_default_scheme(), vars=None, expand=True): | |
| """Return a mapping containing an install scheme. | |
| ``scheme`` is the install scheme name. If not provided, it will | |
| return the default scheme for the current platform. | |
| """ | |
| if expand: | |
| return _expand_vars(scheme, vars) | |
| else: | |
| return _INSTALL_SCHEMES[scheme] | |
| def get_path(name, scheme=get_default_scheme(), vars=None, expand=True): | |
| """Return a path corresponding to the scheme. | |
| ``scheme`` is the install scheme name. | |
| """ | |
| return get_paths(scheme, vars, expand)[name] | |
| def _init_config_vars(): | |
| global _CONFIG_VARS | |
| _CONFIG_VARS = {} | |
| prefix = os.path.normpath(sys.prefix) | |
| exec_prefix = os.path.normpath(sys.exec_prefix) | |
| base_prefix = _BASE_PREFIX | |
| base_exec_prefix = _BASE_EXEC_PREFIX | |
| try: | |
| abiflags = sys.abiflags | |
| except AttributeError: | |
| abiflags = '' | |
| if os.name == 'posix': | |
| _init_posix(_CONFIG_VARS) | |
| # If we are cross-compiling, load the prefixes from the Makefile instead. | |
| if '_PYTHON_PROJECT_BASE' in os.environ: | |
| prefix = _CONFIG_VARS['host_prefix'] | |
| exec_prefix = _CONFIG_VARS['host_exec_prefix'] | |
| base_prefix = _CONFIG_VARS['host_prefix'] | |
| base_exec_prefix = _CONFIG_VARS['host_exec_prefix'] | |
| abiflags = _CONFIG_VARS['ABIFLAGS'] | |
| # Normalized versions of prefix and exec_prefix are handy to have; | |
| # in fact, these are the standard versions used most places in the | |
| # Distutils. | |
| _CONFIG_VARS['prefix'] = prefix | |
| _CONFIG_VARS['exec_prefix'] = exec_prefix | |
| _CONFIG_VARS['py_version'] = _PY_VERSION | |
| _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT | |
| _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT | |
| _CONFIG_VARS['installed_base'] = base_prefix | |
| _CONFIG_VARS['base'] = prefix | |
| _CONFIG_VARS['installed_platbase'] = base_exec_prefix | |
| _CONFIG_VARS['platbase'] = exec_prefix | |
| _CONFIG_VARS['projectbase'] = _PROJECT_BASE | |
| _CONFIG_VARS['platlibdir'] = sys.platlibdir | |
| _CONFIG_VARS['implementation'] = _get_implementation() | |
| _CONFIG_VARS['implementation_lower'] = _get_implementation().lower() | |
| _CONFIG_VARS['abiflags'] = abiflags | |
| try: | |
| _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') | |
| except AttributeError: | |
| _CONFIG_VARS['py_version_nodot_plat'] = '' | |
| if os.name == 'nt': | |
| _init_non_posix(_CONFIG_VARS) | |
| _CONFIG_VARS['VPATH'] = sys._vpath | |
| if _HAS_USER_BASE: | |
| # Setting 'userbase' is done below the call to the | |
| # init function to enable using 'get_config_var' in | |
| # the init-function. | |
| _CONFIG_VARS['userbase'] = _getuserbase() | |
| # e.g., 't' for free-threaded or '' for default build | |
| _CONFIG_VARS['abi_thread'] = 't' if _CONFIG_VARS.get('Py_GIL_DISABLED') else '' | |
| # Always convert srcdir to an absolute path | |
| srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) | |
| if os.name == 'posix': | |
| if _PYTHON_BUILD: | |
| # If srcdir is a relative path (typically '.' or '..') | |
| # then it should be interpreted relative to the directory | |
| # containing Makefile. | |
| base = os.path.dirname(get_makefile_filename()) | |
| srcdir = os.path.join(base, srcdir) | |
| else: | |
| # srcdir is not meaningful since the installation is | |
| # spread about the filesystem. We choose the | |
| # directory containing the Makefile since we know it | |
| # exists. | |
| srcdir = os.path.dirname(get_makefile_filename()) | |
| _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) | |
| # OS X platforms require special customization to handle | |
| # multi-architecture, multi-os-version installers | |
| if sys.platform == 'darwin': | |
| import _osx_support | |
| _osx_support.customize_config_vars(_CONFIG_VARS) | |
| global _CONFIG_VARS_INITIALIZED | |
| _CONFIG_VARS_INITIALIZED = True | |
| def get_config_vars(*args): | |
| """With no arguments, return a dictionary of all configuration | |
| variables relevant for the current platform. | |
| On Unix, this means every variable defined in Python's installed Makefile; | |
| On Windows it's a much smaller set. | |
| With arguments, return a list of values that result from looking up | |
| each argument in the configuration variable dictionary. | |
| """ | |
| global _CONFIG_VARS_INITIALIZED | |
| global _config_vars_cached_prefix | |
| global _config_vars_cached_exec_prefix | |
| # Avoid claiming the lock once initialization is complete. | |
| prefix = os.path.normpath(sys.prefix) | |
| exec_prefix = os.path.normpath(sys.exec_prefix) | |
| if _CONFIG_VARS_INITIALIZED: | |
| # GH-126789: If sys.prefix or sys.exec_prefix were updated, invalidate the cache. | |
| if _config_vars_cached_prefix != prefix or _config_vars_cached_exec_prefix != exec_prefix: | |
| with _CONFIG_VARS_LOCK: | |
| _CONFIG_VARS_INITIALIZED = False | |
| _init_config_vars() | |
| _config_vars_cached_prefix = prefix | |
| _config_vars_cached_exec_prefix = exec_prefix | |
| else: | |
| # Initialize the config_vars cache. | |
| with _CONFIG_VARS_LOCK: | |
| # Test again with the lock held to avoid races. Note that | |
| # we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED, | |
| # to ensure that recursive calls to get_config_vars() | |
| # don't re-enter init_config_vars(). | |
| if _CONFIG_VARS is None: | |
| _init_config_vars() | |
| _config_vars_cached_prefix = prefix | |
| _config_vars_cached_exec_prefix = exec_prefix | |
| if args: | |
| vals = [] | |
| for name in args: | |
| vals.append(_CONFIG_VARS.get(name)) | |
| return vals | |
| else: | |
| return _CONFIG_VARS | |
| def get_config_var(name): | |
| """Return the value of a single variable using the dictionary returned by | |
| 'get_config_vars()'. | |
| Equivalent to get_config_vars().get(name) | |
| """ | |
| return get_config_vars().get(name) | |
| def get_platform(): | |
| """Return a string that identifies the current platform. | |
| This is used mainly to distinguish platform-specific build directories and | |
| platform-specific built distributions. Typically includes the OS name and | |
| version and the architecture (as supplied by 'os.uname()'), although the | |
| exact information included depends on the OS; on Linux, the kernel version | |
| isn't particularly important. | |
| Examples of returned values: | |
| linux-x86_64 | |
| linux-aarch64 | |
| solaris-2.6-sun4u | |
| Windows: | |
| - win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T) | |
| - win-arm64 (64-bit Windows on ARM64, aka AArch64) | |
| - win32 (all others - specifically, sys.platform is returned) | |
| POSIX based OS: | |
| - linux-x86_64 | |
| - macosx-15.5-arm64 | |
| - macosx-26.0-universal2 (macOS on Apple Silicon or Intel) | |
| - android-24-arm64_v8a | |
| For other non-POSIX platforms, currently just returns :data:`sys.platform`.""" | |
| if os.name == 'nt': | |
| import _sysconfig | |
| platform = _sysconfig.get_platform() | |
| if platform: | |
| return platform | |
| return sys.platform | |
| if os.name != "posix" or not hasattr(os, 'uname'): | |
| # XXX what about the architecture? NT is Intel or Alpha | |
| return sys.platform | |
| # Set for cross builds explicitly | |
| if "_PYTHON_HOST_PLATFORM" in os.environ: | |
| osname, _, machine = os.environ["_PYTHON_HOST_PLATFORM"].partition('-') | |
| release = None | |
| else: | |
| # Try to distinguish various flavours of Unix | |
| osname, host, release, version, machine = os.uname() | |
| # Convert the OS name to lowercase, remove '/' characters, and translate | |
| # spaces (for "Power Macintosh") | |
| osname = osname.lower().replace('/', '') | |
| machine = machine.replace(' ', '_') | |
| machine = machine.replace('/', '-') | |
| if osname == "android" or sys.platform == "android": | |
| osname = "android" | |
| release = get_config_var("ANDROID_API_LEVEL") | |
| # Wheel tags use the ABI names from Android's own tools. | |
| # When Python is running on 32-bit ARM Android on a 64-bit ARM kernel, | |
| # 'os.uname().machine' is 'armv8l'. Such devices run the same userspace | |
| # code as 'armv7l' devices. | |
| # During the build process of the Android testbed when targeting 32-bit ARM, | |
| # '_PYTHON_HOST_PLATFORM' is 'arm-linux-androideabi', so 'machine' becomes | |
| # 'arm'. | |
| machine = { | |
| "aarch64": "arm64_v8a", | |
| "arm": "armeabi_v7a", | |
| "armv7l": "armeabi_v7a", | |
| "armv8l": "armeabi_v7a", | |
| "i686": "x86", | |
| "x86_64": "x86_64", | |
| }[machine] | |
| elif osname == "linux": | |
| # At least on Linux/Intel, 'machine' is the processor -- | |
| # i386, etc. | |
| # XXX what about Alpha, SPARC, etc? | |
| return f"{osname}-{machine}" | |
| elif osname[:5] == "sunos": | |
| if release[0] >= "5": # SunOS 5 == Solaris 2 | |
| osname = "solaris" | |
| release = f"{int(release[0]) - 3}.{release[2:]}" | |
| # We can't use "platform.architecture()[0]" because a | |
| # bootstrap problem. We use a dict to get an error | |
| # if some suspicious happens. | |
| bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} | |
| machine += f".{bitness[sys.maxsize]}" | |
| # fall through to standard osname-release-machine representation | |
| elif osname[:3] == "aix": | |
| from _aix_support import aix_platform | |
| return aix_platform() | |
| elif osname[:6] == "cygwin": | |
| osname = "cygwin" | |
| import re | |
| rel_re = re.compile(r'[\d.]+') | |
| m = rel_re.match(release) | |
| if m: | |
| release = m.group() | |
| elif osname[:6] == "darwin": | |
| if sys.platform == "ios": | |
| release = get_config_vars().get("IPHONEOS_DEPLOYMENT_TARGET", "13.0") | |
| osname = sys.platform | |
| machine = sys.implementation._multiarch | |
| else: | |
| import _osx_support | |
| osname, release, machine = _osx_support.get_platform_osx( | |
| get_config_vars(), | |
| osname, release, machine) | |
| return '-'.join(map(str, filter(None, (osname, release, machine)))) | |
| def get_python_version(): | |
| return _PY_VERSION_SHORT | |
| def _get_python_version_abi(): | |
| return _PY_VERSION_SHORT + get_config_var("abi_thread") |