Merge pull request #4569 from youknowone/update-sys-311

Update sysconfig, test_sys and test_syslog from CPython 3.11
This commit is contained in:
Jeong YunWon
2023-02-25 23:01:48 +09:00
committed by GitHub
5 changed files with 740 additions and 286 deletions

457
Lib/sysconfig.py vendored
View File

@@ -18,12 +18,17 @@ __all__ = [
'parse_config_h',
]
# Keys for get_config_var() that are never converted to Python integers.
_ALWAYS_STR = {
'MACOSX_DEPLOYMENT_TARGET',
}
_INSTALL_SCHEMES = {
'posix_prefix': {
'stdlib': '{installed_base}/lib/python{py_version_short}',
'platstdlib': '{platbase}/lib/python{py_version_short}',
'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
'purelib': '{base}/lib/python{py_version_short}/site-packages',
'platlib': '{platbase}/lib/python{py_version_short}/site-packages',
'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
'include':
'{installed_base}/include/python{py_version_short}{abiflags}',
'platinclude':
@@ -51,49 +56,118 @@ _INSTALL_SCHEMES = {
'scripts': '{base}/Scripts',
'data': '{base}',
},
'nt_user': {
'stdlib': '{userbase}/Python{py_version_nodot}',
'platstdlib': '{userbase}/Python{py_version_nodot}',
'purelib': '{userbase}/Python{py_version_nodot}/site-packages',
'platlib': '{userbase}/Python{py_version_nodot}/site-packages',
'include': '{userbase}/Python{py_version_nodot}/Include',
'scripts': '{userbase}/Python{py_version_nodot}/Scripts',
'data': '{userbase}',
# 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}/python{py_version_short}',
'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
'purelib': '{base}/lib/python{py_version_short}/site-packages',
'platlib': '{platbase}/{platlibdir}/python{py_version_short}/site-packages',
'include':
'{installed_base}/include/python{py_version_short}{abiflags}',
'platinclude':
'{installed_platbase}/include/python{py_version_short}{abiflags}',
'scripts': '{base}/bin',
'data': '{base}',
},
'posix_user': {
'stdlib': '{userbase}/lib/python{py_version_short}',
'platstdlib': '{userbase}/lib/python{py_version_short}',
'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
'include': '{userbase}/include/python{py_version_short}',
'scripts': '{userbase}/bin',
'data': '{userbase}',
},
'osx_framework_user': {
'stdlib': '{userbase}/lib/python',
'platstdlib': '{userbase}/lib/python',
'purelib': '{userbase}/lib/python/site-packages',
'platlib': '{userbase}/lib/python/site-packages',
'include': '{userbase}/include',
'scripts': '{userbase}/bin',
'data': '{userbase}',
'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}',
},
}
# XXX RUSTPYTHON: replace python with rustpython in all these paths
for group in _INSTALL_SCHEMES.values():
for key in group.keys():
group[key] = group[key].replace("Python", "RustPython").replace("python", "rustpython")
# 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']
# 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, VxWorks, and WASI have no home directories
if sys.platform in {"emscripten", "vxworks", "wasi"}:
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, "Python")
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}/Python{py_version_nodot_plat}',
'platstdlib': '{userbase}/Python{py_version_nodot_plat}',
'purelib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
'platlib': '{userbase}/Python{py_version_nodot_plat}/site-packages',
'include': '{userbase}/Python{py_version_nodot_plat}/Include',
'scripts': '{userbase}/Python{py_version_nodot_plat}/Scripts',
'data': '{userbase}',
},
'posix_user': {
'stdlib': '{userbase}/{platlibdir}/python{py_version_short}',
'platstdlib': '{userbase}/{platlibdir}/python{py_version_short}',
'purelib': '{userbase}/lib/python{py_version_short}/site-packages',
'platlib': '{userbase}/lib/python{py_version_short}/site-packages',
'include': '{userbase}/include/python{py_version_short}',
'scripts': '{userbase}/bin',
'data': '{userbase}',
},
'osx_framework_user': {
'stdlib': '{userbase}/lib/python',
'platstdlib': '{userbase}/lib/python',
'purelib': '{userbase}/lib/python/site-packages',
'platlib': '{userbase}/lib/python/site-packages',
'include': '{userbase}/include/python{py_version_short}',
'scripts': '{userbase}/bin',
'data': '{userbase}',
},
}
_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
'scripts', 'data')
# FIXME don't rely on sys.version here, its format is an implementation detail
# of CPython, use sys.version_info or sys.hexversion
_PY_VERSION = sys.version.split()[0]
_PY_VERSION_SHORT = '%d.%d' % sys.version_info[:2]
_PY_VERSION_SHORT_NO_DOT = '%d%d' % sys.version_info[:2]
_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]}'
_PREFIX = os.path.normpath(sys.prefix)
_BASE_PREFIX = os.path.normpath(sys.base_prefix)
_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
@@ -101,6 +175,12 @@ _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
_CONFIG_VARS = None
_USER_BASE = None
# Regexes needed for parsing Makefile (and similar syntaxes,
# like old-style Setup files).
_variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)"
_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"
def _safe_realpath(path):
try:
@@ -115,45 +195,60 @@ else:
# unable to retrieve the real program name
_PROJECT_BASE = _safe_realpath(os.getcwd())
if (os.name == 'nt' and
_PROJECT_BASE.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
_PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
# 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_source_dir(d):
for fn in ("Setup.dist", "Setup.local"):
if os.path.isfile(os.path.join(d, "Modules", fn)):
def is_python_build(check_home=None):
if check_home is not None:
import warnings
warnings.warn("check_home argument is deprecated and ignored.",
DeprecationWarning, stacklevel=2)
for fn in ("Setup", "Setup.local"):
if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
return True
return False
_sys_home = getattr(sys, '_home', None)
if (_sys_home and os.name == 'nt' and
_sys_home.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
_sys_home = os.path.dirname(os.path.dirname(_sys_home))
def is_python_build(check_home=False):
if check_home and _sys_home:
return _is_python_source_dir(_sys_home)
return _is_python_source_dir(_PROJECT_BASE)
_PYTHON_BUILD = is_python_build(True)
_PYTHON_BUILD = is_python_build()
if _PYTHON_BUILD:
for scheme in ('posix_prefix', 'posix_home'):
_INSTALL_SCHEMES[scheme]['include'] = '{srcdir}/Include'
_INSTALL_SCHEMES[scheme]['platinclude'] = '{projectbase}/.'
# 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:
except KeyError as var:
try:
return s.format(**os.environ)
except KeyError as var:
raise AttributeError('{%s}' % var)
except KeyError:
raise AttributeError(f'{var}') from None
def _extend_dict(target_dict, other_dict):
target_keys = target_dict.keys()
@@ -168,6 +263,11 @@ def _expand_vars(scheme, vars):
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'):
@@ -176,67 +276,64 @@ def _expand_vars(scheme, vars):
return res
def _get_default_scheme():
if os.name == 'posix':
# the default scheme for posix is posix_prefix
return 'posix_prefix'
return os.name
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 _getuserbase():
env_base = os.environ.get("PYTHONUSERBASE", None)
def joinuser(*args):
return os.path.expanduser(os.path.join(*args))
if os.name == "nt":
base = os.environ.get("APPDATA") or "~"
if env_base:
return env_base
else:
return joinuser(base, "Python")
if sys.platform == "darwin":
framework = get_config_var("PYTHONFRAMEWORK")
if framework:
if env_base:
return env_base
else:
return joinuser("~", "Library", framework, "%d.%d" %
sys.version_info[:2])
if env_base:
return env_base
else:
return joinuser("~", ".local")
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 _parse_makefile(filename, vars=None):
def get_default_scheme():
return get_preferred_scheme('prefix')
def _parse_makefile(filename, vars=None, keep_unresolved=True):
"""Parse a Makefile-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.
"""
# Regexes needed for parsing Makefile (and similar syntaxes,
# like old-style Setup files).
import re
_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
if vars is None:
vars = {}
done = {}
notdone = {}
with open(filename, errors="surrogateescape") as f:
with open(filename, encoding=sys.getfilesystemencoding(),
errors="surrogateescape") as f:
lines = f.readlines()
for line in lines:
if line.startswith('#') or line.strip() == '':
continue
m = _variable_rx.match(line)
m = re.match(_variable_rx, line)
if m:
n, v = m.group(1, 2)
v = v.strip()
@@ -247,6 +344,9 @@ def _parse_makefile(filename, vars=None):
notdone[n] = v
else:
try:
if n in _ALWAYS_STR:
raise ValueError
v = int(v)
except ValueError:
# insert literal `$'
@@ -266,8 +366,8 @@ def _parse_makefile(filename, vars=None):
while len(variables) > 0:
for name in tuple(variables):
value = notdone[name]
m1 = _findvar1_rx.search(value)
m2 = _findvar2_rx.search(value)
m1 = re.search(_findvar1_rx, value)
m2 = re.search(_findvar2_rx, value)
if m1 and m2:
m = m1 if m1.start() < m2.start() else m2
else:
@@ -305,6 +405,8 @@ def _parse_makefile(filename, vars=None):
notdone[name] = value
else:
try:
if name in _ALWAYS_STR:
raise ValueError
value = int(value)
except ValueError:
done[name] = value.strip()
@@ -320,9 +422,12 @@ def _parse_makefile(filename, vars=None):
done[name] = value
else:
# Adds unresolved variables to the done dict.
# This is disabled when called from distutils.sysconfig
if keep_unresolved:
done[name] = value
# bogus variable reference (e.g. "prefix=$/opt/python");
# just drop it since we can't deal
done[name] = value
variables.remove(name)
# strip spurious spaces
@@ -338,23 +443,22 @@ def _parse_makefile(filename, vars=None):
def get_makefile_filename():
"""Return the path of the Makefile."""
if _PYTHON_BUILD:
return os.path.join(_sys_home or _PROJECT_BASE, "Makefile")
return os.path.join(_PROJECT_BASE, "Makefile")
if hasattr(sys, 'abiflags'):
config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, 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 += '-%s' % sys.implementation._multiarch
config_dir_name += f'-{sys.implementation._multiarch}'
return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')
def _get_sysconfigdata_name():
return os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
'_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
abi=sys.abiflags,
platform=sys.platform,
multiarch=getattr(sys.implementation, '_multiarch', ''),
))
multiarch = getattr(sys.implementation, '_multiarch', '')
return os.environ.get(
'_PYTHON_SYSCONFIGDATA_NAME',
f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
)
def _generate_posix_vars():
@@ -366,19 +470,19 @@ def _generate_posix_vars():
try:
_parse_makefile(makefile, vars)
except OSError as e:
msg = "invalid Python installation: unable to open %s" % makefile
msg = f"invalid Python installation: unable to open {makefile}"
if hasattr(e, "strerror"):
msg = msg + " (%s)" % e.strerror
msg = f"{msg} ({e.strerror})"
raise OSError(msg)
# load the installed pyconfig.h:
config_h = get_config_h_filename()
try:
with open(config_h) as f:
with open(config_h, encoding="utf-8") as f:
parse_config_h(f, vars)
except OSError as e:
msg = "invalid Python installation: unable to open %s" % config_h
msg = f"invalid Python installation: unable to open {config_h}"
if hasattr(e, "strerror"):
msg = msg + " (%s)" % e.strerror
msg = f"{msg} ({e.strerror})"
raise OSError(msg)
# On AIX, there are wrong paths to the linker scripts in the Makefile
# -- these paths are relative to the Python source, but when installed
@@ -404,7 +508,7 @@ def _generate_posix_vars():
module.build_time_vars = vars
sys.modules[name] = module
pybuilddir = 'build/lib.%s-%s' % (get_platform(), _PY_VERSION_SHORT)
pybuilddir = f'build/lib.{get_platform()}-{_PY_VERSION_SHORT}'
if hasattr(sys, "gettotalrefcount"):
pybuilddir += '-pydebug'
os.makedirs(pybuilddir, exist_ok=True)
@@ -417,7 +521,7 @@ def _generate_posix_vars():
pprint.pprint(vars, stream=f)
# Create file used for sys.path fixup -- see Modules/getpath.c
with open('pybuilddir.txt', 'w', encoding='ascii') as f:
with open('pybuilddir.txt', 'w', encoding='utf8') as f:
f.write(pybuilddir)
def _init_posix(vars):
@@ -431,13 +535,20 @@ def _init_posix(vars):
def _init_non_posix(vars):
"""Initialize the module as appropriate for NT"""
# set basic install directories
import _imp
vars['LIBDEST'] = get_path('stdlib')
vars['BINLIBDEST'] = get_path('platstdlib')
vars['INCLUDEPY'] = get_path('include')
vars['EXT_SUFFIX'] = '.pyd'
try:
# GH-99201: _imp.extension_suffixes may be empty when
# HAVE_DYNAMIC_LOADING is not set. In this case, don't set EXT_SUFFIX.
vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
except IndexError:
pass
vars['EXE'] = '.exe'
vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
vars['TZPATH'] = ''
#
# public APIs
@@ -465,6 +576,8 @@ def parse_config_h(fp, vars=None):
if m:
n, v = m.group(1, 2)
try:
if n in _ALWAYS_STR:
raise ValueError
v = int(v)
except ValueError:
pass
@@ -480,9 +593,9 @@ def get_config_h_filename():
"""Return the path of pyconfig.h."""
if _PYTHON_BUILD:
if os.name == "nt":
inc_dir = os.path.join(_sys_home or _PROJECT_BASE, "PC")
inc_dir = os.path.join(_PROJECT_BASE, "PC")
else:
inc_dir = _sys_home or _PROJECT_BASE
inc_dir = _PROJECT_BASE
else:
inc_dir = get_path('platinclude')
return os.path.join(inc_dir, 'pyconfig.h')
@@ -498,7 +611,7 @@ def get_path_names():
return _SCHEME_KEYS
def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
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
@@ -510,7 +623,7 @@ def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
return _INSTALL_SCHEMES[scheme]
def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True):
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.
@@ -544,24 +657,27 @@ def get_config_vars(*args):
_CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX
_CONFIG_VARS['platbase'] = _EXEC_PREFIX
_CONFIG_VARS['projectbase'] = _PROJECT_BASE
_CONFIG_VARS['platlibdir'] = sys.platlibdir
try:
_CONFIG_VARS['abiflags'] = sys.abiflags
except AttributeError:
# sys.abiflags may not be defined on all platforms.
_CONFIG_VARS['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 os.name == 'posix':
_init_posix(_CONFIG_VARS)
# For backward compatibility, see issue19555
SO = _CONFIG_VARS.get('EXT_SUFFIX')
if SO is not None:
_CONFIG_VARS['SO'] = SO
# 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()
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()
# Always convert srcdir to an absolute path
srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE)
@@ -601,9 +717,6 @@ def get_config_var(name):
Equivalent to get_config_vars().get(name)
"""
if name == 'SO':
import warnings
warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
return get_config_vars().get(name)
@@ -611,39 +724,30 @@ 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; eg. for IRIX
the architecture isn't particularly important (IRIX only runs on SGI
hardware), but for Linux the kernel version isn't particularly
important.
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-i586
linux-alpha (?)
solaris-2.6-sun4u
irix-5.3
irix64-6.2
Windows will return one of:
win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
win-ia64 (64bit Windows on Itanium)
win32 (all others - specifically, sys.platform is returned)
For other non-POSIX platforms, currently just returns 'sys.platform'.
"""
if os.name == 'nt':
# sniff sys.version for architecture.
prefix = " bit ("
i = sys.version.find(prefix)
if i == -1:
return sys.platform
j = sys.version.find(")", i)
look = sys.version[i+len(prefix):j].lower()
if look == 'amd64':
if 'amd64' in sys.version.lower():
return 'win-amd64'
if look == 'itanium':
return 'win-ia64'
if '(arm)' in sys.version.lower():
return 'win-arm32'
if '(arm64)' in sys.version.lower():
return 'win-arm64'
return sys.platform
if os.name != "posix" or not hasattr(os, 'uname'):
@@ -657,8 +761,8 @@ def get_platform():
# Try to distinguish various flavours of Unix
osname, host, release, version, machine = os.uname()
# Convert the OS name to lowercase, remove '/' characters
# (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")
# Convert the OS name to lowercase, remove '/' characters, and translate
# spaces (for "Power Macintosh")
osname = osname.lower().replace('/', '')
machine = machine.replace(' ', '_')
machine = machine.replace('/', '-')
@@ -667,21 +771,20 @@ def get_platform():
# At least on Linux/Intel, 'machine' is the processor --
# i386, etc.
# XXX what about Alpha, SPARC, etc?
return "%s-%s" % (osname, machine)
return f"{osname}-{machine}"
elif osname[:5] == "sunos":
if release[0] >= "5": # SunOS 5 == Solaris 2
osname = "solaris"
release = "%d.%s" % (int(release[0]) - 3, release[2:])
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 += ".%s" % bitness[sys.maxsize]
machine += f".{bitness[sys.maxsize]}"
# fall through to standard osname-release-machine representation
elif osname[:4] == "irix": # could be "irix64"!
return "%s-%s" % (osname, release)
elif osname[:3] == "aix":
return "%s-%s.%s" % (osname, version, release)
from _aix_support import aix_platform
return aix_platform()
elif osname[:6] == "cygwin":
osname = "cygwin"
import re
@@ -695,18 +798,44 @@ def get_platform():
get_config_vars(),
osname, release, machine)
return "%s-%s-%s" % (osname, release, machine)
return f"{osname}-{release}-{machine}"
def get_python_version():
return _PY_VERSION_SHORT
def expand_makefile_vars(s, vars):
"""Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
'string' according to 'vars' (a dictionary mapping variable names to
values). Variables not present in 'vars' are silently expanded to the
empty string. The variable values in 'vars' should not contain further
variable expansions; if 'vars' is the output of 'parse_makefile()',
you're fine. Returns a variable-expanded version of 's'.
"""
import re
# This algorithm does multiple expansion, so if vars['foo'] contains
# "${bar}", it will expand ${foo} to ${bar}, and then expand
# ${bar}... and so forth. This is fine as long as 'vars' comes from
# 'parse_makefile()', which takes care of such expansions eagerly,
# according to make's variable expansion semantics.
while True:
m = re.search(_findvar1_rx, s) or re.search(_findvar2_rx, s)
if m:
(beg, end) = m.span()
s = s[0:beg] + vars.get(m.group(1)) + s[end:]
else:
break
return s
def _print_dict(title, data):
for index, (key, value) in enumerate(sorted(data.items())):
if index == 0:
print('%s: ' % (title))
print('\t%s = "%s"' % (key, value))
print(f'{title}: ')
print(f'\t{key} = "{value}"')
def _main():
@@ -714,9 +843,9 @@ def _main():
if '--generate-posix-vars' in sys.argv:
_generate_posix_vars()
return
print('Platform: "%s"' % get_platform())
print('Python version: "%s"' % get_python_version())
print('Current installation scheme: "%s"' % _get_default_scheme())
print(f'Platform: "{get_platform()}"')
print(f'Python version: "{get_python_version()}"')
print(f'Current installation scheme: "{get_default_scheme()}"')
print()
_print_dict('Paths', get_paths())
print()

237
Lib/test/test_sys.py vendored
View File

@@ -1,6 +1,6 @@
import builtins
import codecs
# import gc
import gc
import locale
import operator
import os
@@ -13,6 +13,7 @@ from test import support
from test.support import os_helper
from test.support.script_helper import assert_python_ok, assert_python_failure
from test.support import threading_helper
from test.support import import_helper
import textwrap
import unittest
import warnings
@@ -22,6 +23,7 @@ import warnings
# strings to intern in test_intern()
INTERN_NUMRUNS = 0
DICT_KEY_STRUCT_FORMAT = 'n2BI2n'
class DisplayHookTest(unittest.TestCase):
@@ -69,6 +71,75 @@ class DisplayHookTest(unittest.TestCase):
code = compile("42", "<string>", "single")
self.assertRaises(ValueError, eval, code)
class ActiveExceptionTests(unittest.TestCase):
def test_exc_info_no_exception(self):
self.assertEqual(sys.exc_info(), (None, None, None))
# TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'exception'
@unittest.expectedFailure
def test_sys_exception_no_exception(self):
self.assertEqual(sys.exception(), None)
def test_exc_info_with_exception_instance(self):
def f():
raise ValueError(42)
try:
f()
except Exception as e_:
e = e_
exc_info = sys.exc_info()
self.assertIsInstance(e, ValueError)
self.assertIs(exc_info[0], ValueError)
self.assertIs(exc_info[1], e)
self.assertIs(exc_info[2], e.__traceback__)
def test_exc_info_with_exception_type(self):
def f():
raise ValueError
try:
f()
except Exception as e_:
e = e_
exc_info = sys.exc_info()
self.assertIsInstance(e, ValueError)
self.assertIs(exc_info[0], ValueError)
self.assertIs(exc_info[1], e)
self.assertIs(exc_info[2], e.__traceback__)
# TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'exception'
@unittest.expectedFailure
def test_sys_exception_with_exception_instance(self):
def f():
raise ValueError(42)
try:
f()
except Exception as e_:
e = e_
exc = sys.exception()
self.assertIsInstance(e, ValueError)
self.assertIs(exc, e)
# TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'exception'
@unittest.expectedFailure
def test_sys_exception_with_exception_type(self):
def f():
raise ValueError
try:
f()
except Exception as e_:
e = e_
exc = sys.exception()
self.assertIsInstance(e, ValueError)
self.assertIs(exc, e)
class ExceptHookTest(unittest.TestCase):
@@ -351,6 +422,7 @@ class SysModuleTest(unittest.TestCase):
# XXX RUSTPYTHON: above comment is from original cpython test; not sure why the cpython_only decorator wasn't added
@test.support.cpython_only
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_current_frames(self):
import threading
import traceback
@@ -418,6 +490,7 @@ class SysModuleTest(unittest.TestCase):
# TODO: RUSTPYTHON, AttributeError: module 'sys' has no attribute '_current_exceptions'
@unittest.expectedFailure
@threading_helper.reap_threads
@threading_helper.requires_working_threading()
def test_current_exceptions(self):
import threading
import traceback
@@ -500,11 +573,17 @@ class SysModuleTest(unittest.TestCase):
self.assertIsInstance(sys.executable, str)
self.assertEqual(len(sys.float_info), 11)
self.assertEqual(sys.float_info.radix, 2)
self.assertEqual(len(sys.int_info), 2)
self.assertEqual(len(sys.int_info), 4)
self.assertTrue(sys.int_info.bits_per_digit % 5 == 0)
self.assertTrue(sys.int_info.sizeof_digit >= 1)
self.assertGreaterEqual(sys.int_info.default_max_str_digits, 500)
self.assertGreaterEqual(sys.int_info.str_digits_check_threshold, 100)
self.assertGreater(sys.int_info.default_max_str_digits,
sys.int_info.str_digits_check_threshold)
self.assertEqual(type(sys.int_info.bits_per_digit), int)
self.assertEqual(type(sys.int_info.sizeof_digit), int)
self.assertIsInstance(sys.int_info.default_max_str_digits, int)
self.assertIsInstance(sys.int_info.str_digits_check_threshold, int)
self.assertIsInstance(sys.hexversion, int)
self.assertEqual(len(sys.hash_info), 9)
@@ -523,7 +602,7 @@ class SysModuleTest(unittest.TestCase):
self.assertIsInstance(sys.hash_info.nan, int)
self.assertIsInstance(sys.hash_info.imag, int)
algo = sysconfig.get_config_var("Py_HASH_ALGORITHM")
if sys.hash_info.algorithm in {"fnv", "siphash24"}:
if sys.hash_info.algorithm in {"fnv", "siphash13", "siphash24"}:
self.assertIn(sys.hash_info.hash_bits, {32, 64})
self.assertIn(sys.hash_info.seed_bits, {32, 64, 128})
@@ -531,8 +610,10 @@ class SysModuleTest(unittest.TestCase):
self.assertEqual(sys.hash_info.algorithm, "siphash24")
elif algo == 2:
self.assertEqual(sys.hash_info.algorithm, "fnv")
elif algo == 3:
self.assertEqual(sys.hash_info.algorithm, "siphash13")
else:
self.assertIn(sys.hash_info.algorithm, {"fnv", "siphash24"})
self.assertIn(sys.hash_info.algorithm, {"fnv", "siphash13", "siphash24"})
else:
# PY_HASH_EXTERNAL
self.assertEqual(algo, 0)
@@ -574,8 +655,24 @@ class SysModuleTest(unittest.TestCase):
def test_thread_info(self):
info = sys.thread_info
self.assertEqual(len(info), 3)
self.assertIn(info.name, ('nt', 'pthread', 'solaris', None))
self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None))
self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))
if sys.platform.startswith(("linux", "freebsd")):
self.assertEqual(info.name, "pthread")
elif sys.platform == "win32":
self.assertEqual(info.name, "nt")
elif sys.platform == "emscripten":
self.assertIn(info.name, {"pthread", "pthread-stubs"})
elif sys.platform == "wasi":
self.assertEqual(info.name, "pthread-stubs")
@unittest.skipUnless(support.is_emscripten, "only available on Emscripten")
def test_emscripten_info(self):
self.assertEqual(len(sys._emscripten_info), 4)
self.assertIsInstance(sys._emscripten_info.emscripten_version, tuple)
self.assertIsInstance(sys._emscripten_info.runtime, (str, type(None)))
self.assertIsInstance(sys._emscripten_info.pthreads, bool)
self.assertIsInstance(sys._emscripten_info.shared_memory, bool)
def test_43581(self):
# Can't use sys.stdout, as this is a StringIO object when
@@ -609,10 +706,10 @@ class SysModuleTest(unittest.TestCase):
"dont_write_bytecode", "no_user_site", "no_site",
"ignore_environment", "verbose", "bytes_warning", "quiet",
"hash_randomization", "isolated", "dev_mode", "utf8_mode",
"warn_default_encoding")
"warn_default_encoding", "safe_path", "int_max_str_digits")
for attr in attrs:
self.assertTrue(hasattr(sys.flags, attr), attr)
attr_type = bool if attr == "dev_mode" else int
attr_type = bool if attr in ("dev_mode", "safe_path") else int
self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
self.assertTrue(repr(sys.flags))
self.assertEqual(len(sys.flags), len(attrs))
@@ -648,6 +745,7 @@ class SysModuleTest(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@support.requires_subprocess()
def test_ioencoding(self):
env = dict(os.environ)
@@ -695,6 +793,7 @@ class SysModuleTest(unittest.TestCase):
'requires OS support of non-ASCII encodings')
@unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False),
'requires FS encoding to match locale')
@support.requires_subprocess()
def test_ioencoding_nonascii(self):
env = dict(os.environ)
@@ -707,6 +806,7 @@ class SysModuleTest(unittest.TestCase):
@unittest.skipIf(sys.base_prefix != sys.prefix,
'Test is not venv-compatible')
@support.requires_subprocess()
def test_executable(self):
# sys.executable should be absolute
self.assertEqual(os.path.abspath(sys.executable), sys.executable)
@@ -810,11 +910,13 @@ class SysModuleTest(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@support.requires_subprocess()
def test_c_locale_surrogateescape(self):
self.check_locale_surrogateescape('C')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@support.requires_subprocess()
def test_posix_locale_surrogateescape(self):
self.check_locale_surrogateescape('POSIX')
@@ -846,7 +948,18 @@ class SysModuleTest(unittest.TestCase):
from test.support.script_helper import assert_python_ok
args = ['-c', 'import sys; sys._debugmallocstats()']
ret, out, err = assert_python_ok(*args)
self.assertIn(b"free PyDictObjects", err)
# Output of sys._debugmallocstats() depends on configure flags.
# The sysconfig vars are not available on Windows.
if sys.platform != "win32":
with_freelists = sysconfig.get_config_var("WITH_FREELISTS")
with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
if with_freelists:
self.assertIn(b"free PyDictObjects", err)
if with_pymalloc:
self.assertIn(b'Small block threshold', err)
if not with_freelists and not with_pymalloc:
self.assertFalse(err)
# The function has no parameter
self.assertRaises(TypeError, sys._debugmallocstats, True)
@@ -958,6 +1071,7 @@ class SysModuleTest(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@support.requires_subprocess()
def test_sys_tracebacklimit(self):
code = """if 1:
import sys
@@ -1004,6 +1118,7 @@ class SysModuleTest(unittest.TestCase):
out = out.decode('ascii', 'replace').rstrip()
self.assertEqual(out, 'mbcs replace')
@support.requires_subprocess()
def test_orig_argv(self):
code = textwrap.dedent('''
import sys
@@ -1026,6 +1141,17 @@ class SysModuleTest(unittest.TestCase):
for name in sys.stdlib_module_names:
self.assertIsInstance(name, str)
# TODO: RUSTPYTHON, AttributeError: module 'sys' has no attribute '_stdlib_dir'
@unittest.expectedFailure
def test_stdlib_dir(self):
os = import_helper.import_fresh_module('os')
marker = getattr(os, '__file__', None)
if marker and not os.path.exists(marker):
marker = None
expected = os.path.dirname(marker) if marker else None
self.assertEqual(os.path.normpath(sys._stdlib_dir),
os.path.normpath(expected))
@test.support.cpython_only
class UnraisableHookTest(unittest.TestCase):
@@ -1103,19 +1229,30 @@ class UnraisableHookTest(unittest.TestCase):
self.assertTrue(report.endswith("\n"))
def test_original_unraisablehook_exception_qualname(self):
# See bpo-41031, bpo-45083.
# Check that the exception is printed with its qualified name
# rather than just classname, and the module names appears
# unless it is one of the hard-coded exclusions.
class A:
class B:
class X(Exception):
pass
with test.support.captured_stderr() as stderr, \
test.support.swap_attr(sys, 'unraisablehook',
sys.__unraisablehook__):
expected = self.write_unraisable_exc(
A.B.X(), "msg", "obj");
report = stderr.getvalue()
testName = 'test_original_unraisablehook_exception_qualname'
self.assertIn(f"{testName}.<locals>.A.B.X", report)
for moduleName in 'builtins', '__main__', 'some_module':
with self.subTest(moduleName=moduleName):
A.B.X.__module__ = moduleName
with test.support.captured_stderr() as stderr, test.support.swap_attr(
sys, 'unraisablehook', sys.__unraisablehook__
):
expected = self.write_unraisable_exc(
A.B.X(), "msg", "obj"
)
report = stderr.getvalue()
self.assertIn(A.B.X.__qualname__, report)
if moduleName in ['builtins', '__main__']:
self.assertNotIn(moduleName + '.', report)
else:
self.assertIn(moduleName + '.', report)
def test_original_unraisablehook_wrong_type(self):
exc = ValueError(42)
@@ -1226,6 +1363,7 @@ class SizeofTest(unittest.TestCase):
check = self.check_sizeof
# bool
check(True, vsize('') + self.longdigit)
check(False, vsize('') + self.longdigit)
# buffer
# XXX
# builtin_function_or_method
@@ -1275,10 +1413,14 @@ class SizeofTest(unittest.TestCase):
check({}.__iter__, size('2P'))
# empty dict
check({}, size('nQ2P'))
# dict
check({"a": 1}, size('nQ2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P'))
# dict (string key)
check({"a": 1}, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + (8*2//3)*calcsize('2P'))
longdict = {str(i): i for i in range(8)}
check(longdict, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + (16*2//3)*calcsize('2P'))
# dict (non-string key)
check({1: 1}, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 8 + (8*2//3)*calcsize('n2P'))
longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
check(longdict, size('nQ2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P'))
check(longdict, size('nQ2P') + calcsize(DICT_KEY_STRUCT_FORMAT) + 16 + (16*2//3)*calcsize('n2P'))
# dictionary-keyview
check({}.keys(), size('P'))
# dictionary-valueview
@@ -1297,13 +1439,13 @@ class SizeofTest(unittest.TestCase):
class C(object): pass
check(C.__dict__, size('P'))
# BaseException
check(BaseException(), size('5Pb'))
check(BaseException(), size('6Pb'))
# UnicodeEncodeError
check(UnicodeEncodeError("", "", 0, 0, ""), size('5Pb 2P2nP'))
check(UnicodeEncodeError("", "", 0, 0, ""), size('6Pb 2P2nP'))
# UnicodeDecodeError
check(UnicodeDecodeError("", b"", 0, 0, ""), size('5Pb 2P2nP'))
check(UnicodeDecodeError("", b"", 0, 0, ""), size('6Pb 2P2nP'))
# UnicodeTranslateError
check(UnicodeTranslateError("", 0, 1, ""), size('5Pb 2P2nP'))
check(UnicodeTranslateError("", 0, 1, ""), size('6Pb 2P2nP'))
# ellipses
check(Ellipsis, size(''))
# EncodingMap
@@ -1311,7 +1453,7 @@ class SizeofTest(unittest.TestCase):
x = codecs.charmap_build(encodings.iso8859_3.decoding_table)
check(x, size('32B2iB'))
# enumerate
check(enumerate([]), size('n3P'))
check(enumerate([]), size('n4P'))
# reverse
check(reversed(''), size('nP'))
# float
@@ -1319,17 +1461,13 @@ class SizeofTest(unittest.TestCase):
# sys.floatinfo
check(sys.float_info, vsize('') + self.P * len(sys.float_info))
# frame
import inspect
CO_MAXBLOCKS = 20
x = inspect.currentframe()
ncells = len(x.f_code.co_cellvars)
nfrees = len(x.f_code.co_freevars)
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
ncells + nfrees - 1
check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
def func():
return sys._getframe()
x = func()
check(x, size('3Pi3c7P2ic??2P'))
# function
def func(): pass
check(func, size('14P'))
check(func, size('14Pi'))
class c():
@staticmethod
def foo():
@@ -1343,16 +1481,17 @@ class SizeofTest(unittest.TestCase):
check(bar, size('PP'))
# generator
def get_gen(): yield 1
check(get_gen(), size('P2PPP4P'))
check(get_gen(), size('P2P4P4c7P2ic??P'))
# iterator
check(iter('abc'), size('lP'))
# callable-iterator
import re
check(re.finditer('',''), size('2P'))
# list
samples = [[], [1,2,3], ['1', '2', '3']]
for sample in samples:
check(list(sample), vsize('Pn') + len(sample)*self.P)
check(list([]), vsize('Pn'))
check(list([1]), vsize('Pn') + 2*self.P)
check(list([1, 2]), vsize('Pn') + 2*self.P)
check(list([1, 2, 3]), vsize('Pn') + 4*self.P)
# sortwrapper (list)
# XXX
# cmpwrapper (list)
@@ -1362,7 +1501,7 @@ class SizeofTest(unittest.TestCase):
# listreverseiterator (list)
check(reversed([]), size('nP'))
# int
check(0, vsize(''))
check(0, vsize('') + self.longdigit)
check(1, vsize('') + self.longdigit)
check(-1, vsize('') + self.longdigit)
PyLong_BASE = 2**sys.int_info.bits_per_digit
@@ -1424,8 +1563,8 @@ class SizeofTest(unittest.TestCase):
check((1,2,3), vsize('') + 3*self.P)
# type
# static type: PyTypeObject
fmt = 'P2nPI13Pl4Pn9Pn11PIPP'
s = vsize(fmt)
fmt = 'P2nPI13Pl4Pn9Pn12PIP'
s = vsize('2P' + fmt)
check(int, s)
# class
s = vsize(fmt + # PyTypeObject
@@ -1434,18 +1573,21 @@ class SizeofTest(unittest.TestCase):
'3P' # PyMappingMethods
'10P' # PySequenceMethods
'2P' # PyBufferProcs
'5P')
'6P'
'1P' # Specializer cache
)
class newstyleclass(object): pass
# Separate block for PyDictKeysObject with 8 keys and 5 entries
check(newstyleclass, s + calcsize("2nP2n0P") + 8 + 5*calcsize("n2P"))
check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 64 + 42*calcsize("2P"))
# dict with shared keys
check(newstyleclass().__dict__, size('nQ2P') + 5*self.P)
[newstyleclass() for _ in range(100)]
check(newstyleclass().__dict__, size('nQ2P') + self.P)
o = newstyleclass()
o.a = o.b = o.c = o.d = o.e = o.f = o.g = o.h = 1
# Separate block for PyDictKeysObject with 16 keys and 10 entries
check(newstyleclass, s + calcsize("2nP2n0P") + 16 + 10*calcsize("n2P"))
check(newstyleclass, s + calcsize(DICT_KEY_STRUCT_FORMAT) + 64 + 42*calcsize("2P"))
# dict with shared keys
check(newstyleclass().__dict__, size('nQ2P') + 10*self.P)
check(newstyleclass().__dict__, size('nQ2P') + self.P)
# unicode
# each tuple contains a string and its expected character size
# don't put any static strings here, as they may contain
@@ -1453,6 +1595,7 @@ class SizeofTest(unittest.TestCase):
samples = ['1'*100, '\xff'*50,
'\u0100'*40, '\uffff'*100,
'\U00010000'*30, '\U0010ffff'*100]
# also update field definitions in test_unicode.test_raiseMemError
asciifields = "nnbP"
compactfields = asciifields + "nPn"
unicodefields = compactfields + "P"
@@ -1478,11 +1621,11 @@ class SizeofTest(unittest.TestCase):
# TODO: add check that forces layout of unicodefields
# weakref
import weakref
check(weakref.ref(int), size('2Pn2P'))
check(weakref.ref(int), size('2Pn3P'))
# weakproxy
# XXX
# weakcallableproxy
check(weakref.proxy(int), size('2Pn2P'))
check(weakref.proxy(int), size('2Pn3P'))
def check_slots(self, obj, base, extra):
expected = sys.getsizeof(base) + struct.calcsize(extra)

View File

@@ -5,19 +5,25 @@ import subprocess
import shutil
from copy import copy
from test.support import captured_stdout, PythonSymlink
from test.support import (
captured_stdout, PythonSymlink, requires_subprocess, is_wasi
)
from test.support.import_helper import import_module
from test.support.os_helper import (TESTFN, unlink, skip_unless_symlink,
change_cwd)
from test.support.import_helper import import_module
from test.support.warnings_helper import check_warnings
import sysconfig
from sysconfig import (get_paths, get_platform, get_config_vars,
get_path, get_path_names, _INSTALL_SCHEMES,
_get_default_scheme, _expand_vars,
get_scheme_names, get_config_var, _main)
get_default_scheme, get_scheme_names, get_config_var,
_expand_vars, _get_preferred_schemes, _main)
import _osx_support
HAS_USER_BASE = sysconfig._HAS_USER_BASE
class TestSysConfig(unittest.TestCase):
def setUp(self):
@@ -90,17 +96,124 @@ class TestSysConfig(unittest.TestCase):
def test_get_paths(self):
scheme = get_paths()
default_scheme = _get_default_scheme()
default_scheme = get_default_scheme()
wanted = _expand_vars(default_scheme, None)
wanted = sorted(wanted.items())
scheme = sorted(scheme.items())
self.assertEqual(scheme, wanted)
def test_get_path(self):
# XXX make real tests here
config_vars = get_config_vars()
if os.name == 'nt':
# On Windows, we replace the native platlibdir name with the
# default so that POSIX schemes resolve correctly
config_vars = config_vars | {'platlibdir': 'lib'}
for scheme in _INSTALL_SCHEMES:
for name in _INSTALL_SCHEMES[scheme]:
res = get_path(name, scheme)
expected = _INSTALL_SCHEMES[scheme][name].format(**config_vars)
self.assertEqual(
os.path.normpath(get_path(name, scheme)),
os.path.normpath(expected),
)
def test_get_default_scheme(self):
self.assertIn(get_default_scheme(), _INSTALL_SCHEMES)
def test_get_preferred_schemes(self):
expected_schemes = {'prefix', 'home', 'user'}
# Windows.
os.name = 'nt'
schemes = _get_preferred_schemes()
self.assertIsInstance(schemes, dict)
self.assertEqual(set(schemes), expected_schemes)
# Mac and Linux, shared library build.
os.name = 'posix'
schemes = _get_preferred_schemes()
self.assertIsInstance(schemes, dict)
self.assertEqual(set(schemes), expected_schemes)
# Mac, framework build.
os.name = 'posix'
sys.platform = 'darwin'
sys._framework = True
self.assertIsInstance(schemes, dict)
self.assertEqual(set(schemes), expected_schemes)
def test_posix_venv_scheme(self):
# The following directories were hardcoded in the venv module
# before bpo-45413, here we assert the posix_venv scheme does not regress
binpath = 'bin'
incpath = 'include'
libpath = os.path.join('lib',
'python%d.%d' % sys.version_info[:2],
'site-packages')
# Resolve the paths in prefix
binpath = os.path.join(sys.prefix, binpath)
incpath = os.path.join(sys.prefix, incpath)
libpath = os.path.join(sys.prefix, libpath)
self.assertEqual(binpath, sysconfig.get_path('scripts', scheme='posix_venv'))
self.assertEqual(libpath, sysconfig.get_path('purelib', scheme='posix_venv'))
# The include directory on POSIX isn't exactly the same as before,
# but it is "within"
sysconfig_includedir = sysconfig.get_path('include', scheme='posix_venv')
self.assertTrue(sysconfig_includedir.startswith(incpath + os.sep))
# TODO: RUSTPYTHON
if sys.platform == "win32":
test_posix_venv_scheme = unittest.expectedFailure(test_posix_venv_scheme)
def test_nt_venv_scheme(self):
# The following directories were hardcoded in the venv module
# before bpo-45413, here we assert the posix_venv scheme does not regress
binpath = 'Scripts'
incpath = 'Include'
libpath = os.path.join('Lib', 'site-packages')
# Resolve the paths in prefix
binpath = os.path.join(sys.prefix, binpath)
incpath = os.path.join(sys.prefix, incpath)
libpath = os.path.join(sys.prefix, libpath)
self.assertEqual(binpath, sysconfig.get_path('scripts', scheme='nt_venv'))
self.assertEqual(incpath, sysconfig.get_path('include', scheme='nt_venv'))
self.assertEqual(libpath, sysconfig.get_path('purelib', scheme='nt_venv'))
# TODO: RUSTPYTHON
if sys.platform == "win32":
test_nt_venv_scheme = unittest.expectedFailure(test_nt_venv_scheme)
def test_venv_scheme(self):
if sys.platform == 'win32':
self.assertEqual(
sysconfig.get_path('scripts', scheme='venv'),
sysconfig.get_path('scripts', scheme='nt_venv')
)
self.assertEqual(
sysconfig.get_path('include', scheme='venv'),
sysconfig.get_path('include', scheme='nt_venv')
)
self.assertEqual(
sysconfig.get_path('purelib', scheme='venv'),
sysconfig.get_path('purelib', scheme='nt_venv')
)
else:
self.assertEqual(
sysconfig.get_path('scripts', scheme='venv'),
sysconfig.get_path('scripts', scheme='posix_venv')
)
self.assertEqual(
sysconfig.get_path('include', scheme='venv'),
sysconfig.get_path('include', scheme='posix_venv')
)
self.assertEqual(
sysconfig.get_path('purelib', scheme='venv'),
sysconfig.get_path('purelib', scheme='posix_venv')
)
def test_get_config_vars(self):
cvars = get_config_vars()
@@ -227,16 +340,19 @@ class TestSysConfig(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
def test_get_config_h_filename(self):
config_h = sysconfig.get_config_h_filename()
self.assertTrue(os.path.isfile(config_h), config_h)
def test_get_scheme_names(self):
wanted = ('nt', 'nt_user', 'osx_framework_user',
'posix_home', 'posix_prefix', 'posix_user')
self.assertEqual(get_scheme_names(), wanted)
wanted = ['nt', 'posix_home', 'posix_prefix', 'posix_venv', 'nt_venv', 'venv']
if HAS_USER_BASE:
wanted.extend(['nt_user', 'osx_framework_user', 'posix_user'])
self.assertEqual(get_scheme_names(), tuple(sorted(wanted)))
@skip_unless_symlink
@requires_subprocess()
def test_symlink(self): # Issue 7880
with PythonSymlink() as py:
cmd = "-c", "import sysconfig; print(sysconfig.get_platform())"
@@ -250,7 +366,8 @@ class TestSysConfig(unittest.TestCase):
# Issue #8759: make sure the posix scheme for the users
# is similar to the global posix_prefix one
base = get_config_var('base')
user = get_config_var('userbase')
if HAS_USER_BASE:
user = get_config_var('userbase')
# the global scheme mirrors the distinction between prefix and
# exec-prefix but not the user scheme, so we have to adapt the paths
# before comparing (issue #9100)
@@ -265,8 +382,19 @@ class TestSysConfig(unittest.TestCase):
# before comparing
global_path = global_path.replace(sys.base_prefix, sys.prefix)
base = base.replace(sys.base_prefix, sys.prefix)
user_path = get_path(name, 'posix_user')
self.assertEqual(user_path, global_path.replace(base, user, 1))
if HAS_USER_BASE:
user_path = get_path(name, 'posix_user')
expected = os.path.normpath(global_path.replace(base, user, 1))
# bpo-44860: platlib of posix_user doesn't use sys.platlibdir,
# whereas posix_prefix does.
if name == 'platlib':
# Replace "/lib64/python3.11/site-packages" suffix
# with "/lib/python3.11/site-packages".
py_version_short = sysconfig.get_python_version()
suffix = f'python{py_version_short}/site-packages'
expected = expected.replace(f'/{sys.platlibdir}/{suffix}',
f'/lib/{suffix}')
self.assertEqual(user_path, expected)
def test_main(self):
# just making sure _main() runs and returns things in the stdout
@@ -284,6 +412,7 @@ class TestSysConfig(unittest.TestCase):
self.assertIn(ldflags, ldshared)
@unittest.skipUnless(sys.platform == "darwin", "test only relevant on MacOSX")
@requires_subprocess()
def test_platform_in_subprocess(self):
my_platform = sysconfig.get_platform()
@@ -326,6 +455,7 @@ class TestSysConfig(unittest.TestCase):
self.assertEqual(status, 0)
self.assertEqual(my_platform, test_platform)
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
def test_srcdir(self):
# See Issues #15322, #15364.
srcdir = sysconfig.get_config_var('srcdir')
@@ -338,7 +468,11 @@ class TestSysConfig(unittest.TestCase):
# should be a full source checkout.
Python_h = os.path.join(srcdir, 'Include', 'Python.h')
self.assertTrue(os.path.exists(Python_h), Python_h)
self.assertTrue(sysconfig._is_python_source_dir(srcdir))
# <srcdir>/PC/pyconfig.h always exists even if unused on POSIX.
pyconfig_h = os.path.join(srcdir, 'PC', 'pyconfig.h')
self.assertTrue(os.path.exists(pyconfig_h), pyconfig_h)
pyconfig_h_in = os.path.join(srcdir, 'pyconfig.h.in')
self.assertTrue(os.path.exists(pyconfig_h_in), pyconfig_h_in)
elif os.name == 'posix':
makefile_dir = os.path.dirname(sysconfig.get_makefile_filename())
# Issue #19340: srcdir has been realpath'ed already
@@ -357,28 +491,15 @@ class TestSysConfig(unittest.TestCase):
srcdir2 = sysconfig.get_config_var('srcdir')
self.assertEqual(srcdir, srcdir2)
@unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
'EXT_SUFFIX required for this test')
def test_SO_deprecation(self):
self.assertWarns(DeprecationWarning,
sysconfig.get_config_var, 'SO')
@unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
'EXT_SUFFIX required for this test')
def test_SO_value(self):
with check_warnings(('', DeprecationWarning)):
self.assertEqual(sysconfig.get_config_var('SO'),
sysconfig.get_config_var('EXT_SUFFIX'))
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
'EXT_SUFFIX required for this test')
def test_EXT_SUFFIX_in_vars(self):
import _imp
if not _imp.extension_suffixes():
self.skipTest("stub loader has no suffixes")
vars = sysconfig.get_config_vars()
self.assertIsNotNone(vars['SO'])
self.assertEqual(vars['SO'], vars['EXT_SUFFIX'])
self.assertEqual(vars['EXT_SUFFIX'], _imp.extension_suffixes()[0])
@unittest.skipUnless(sys.platform == 'linux' and
@@ -393,11 +514,11 @@ class TestSysConfig(unittest.TestCase):
self.assertTrue('linux' in suffix, suffix)
if re.match('(i[3-6]86|x86_64)$', machine):
if ctypes.sizeof(ctypes.c_char_p()) == 4:
self.assertTrue(suffix.endswith('i386-linux-gnu.so') or
suffix.endswith('x86_64-linux-gnux32.so'),
suffix)
expected_suffixes = 'i386-linux-gnu.so', 'x86_64-linux-gnux32.so', 'i386-linux-musl.so'
else: # 8 byte pointer size
self.assertTrue(suffix.endswith('x86_64-linux-gnu.so'), suffix)
expected_suffixes = 'x86_64-linux-gnu.so', 'x86_64-linux-musl.so'
self.assertTrue(suffix.endswith(expected_suffixes),
f'unexpected suffix {suffix!r}')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@@ -412,6 +533,7 @@ class MakefileTests(unittest.TestCase):
@unittest.expectedFailure
@unittest.skipIf(sys.platform.startswith('win'),
'Test is not Windows compatible')
@unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds")
def test_get_makefile_filename(self):
makefile = sysconfig.get_makefile_filename()
self.assertTrue(os.path.isfile(makefile), makefile)

View File

@@ -1,42 +1,89 @@
from test.support import import_helper
syslog = import_helper.import_module("syslog") #skip if not supported
import unittest
# XXX(nnorwitz): This test sucks. I don't know of a platform independent way
# to verify that the messages were really logged.
# The only purpose of this test is to verify the code doesn't crash or leak.
class Test(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_openlog(self):
syslog.openlog('python')
# Issue #6697.
self.assertRaises(UnicodeEncodeError, syslog.openlog, '\uD800')
def test_syslog(self):
syslog.openlog('python')
syslog.syslog('test message from python test_syslog')
syslog.syslog(syslog.LOG_ERR, 'test error from python test_syslog')
def test_closelog(self):
syslog.openlog('python')
syslog.closelog()
def test_setlogmask(self):
syslog.setlogmask(syslog.LOG_DEBUG)
def test_log_mask(self):
syslog.LOG_MASK(syslog.LOG_INFO)
def test_log_upto(self):
syslog.LOG_UPTO(syslog.LOG_INFO)
def test_openlog_noargs(self):
syslog.openlog()
syslog.syslog('test message from python test_syslog')
if __name__ == "__main__":
unittest.main()
from test.support import import_helper, threading_helper
syslog = import_helper.import_module("syslog") #skip if not supported
from test import support
import sys
import threading
import time
import unittest
# XXX(nnorwitz): This test sucks. I don't know of a platform independent way
# to verify that the messages were really logged.
# The only purpose of this test is to verify the code doesn't crash or leak.
class Test(unittest.TestCase):
def tearDown(self):
syslog.closelog()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_openlog(self):
syslog.openlog('python')
# Issue #6697.
self.assertRaises(UnicodeEncodeError, syslog.openlog, '\uD800')
def test_syslog(self):
syslog.openlog('python')
syslog.syslog('test message from python test_syslog')
syslog.syslog(syslog.LOG_ERR, 'test error from python test_syslog')
def test_syslog_implicit_open(self):
syslog.closelog() # Make sure log is closed
syslog.syslog('test message from python test_syslog')
syslog.syslog(syslog.LOG_ERR, 'test error from python test_syslog')
def test_closelog(self):
syslog.openlog('python')
syslog.closelog()
syslog.closelog() # idempotent operation
def test_setlogmask(self):
mask = syslog.LOG_UPTO(syslog.LOG_WARNING)
oldmask = syslog.setlogmask(mask)
self.assertEqual(syslog.setlogmask(0), mask)
self.assertEqual(syslog.setlogmask(oldmask), mask)
# TODO: RUSTPYTHON; AssertionError: 12 is not false
@unittest.expectedFailure
def test_log_mask(self):
mask = syslog.LOG_UPTO(syslog.LOG_WARNING)
self.assertTrue(mask & syslog.LOG_MASK(syslog.LOG_WARNING))
self.assertTrue(mask & syslog.LOG_MASK(syslog.LOG_ERR))
self.assertFalse(mask & syslog.LOG_MASK(syslog.LOG_INFO))
def test_openlog_noargs(self):
syslog.openlog()
syslog.syslog('test message from python test_syslog')
# TODO: RUSTPYTHON; AttributeError: module 'sys' has no attribute 'getswitchinterval'
@unittest.expectedFailure
@threading_helper.requires_working_threading()
def test_syslog_threaded(self):
start = threading.Event()
stop = False
def opener():
start.wait(10)
i = 1
while not stop:
syslog.openlog(f'python-test-{i}') # new string object
i += 1
def logger():
start.wait(10)
while not stop:
syslog.syslog('test message from python test_syslog')
orig_si = sys.getswitchinterval()
support.setswitchinterval(1e-9)
try:
threads = [threading.Thread(target=opener)]
threads += [threading.Thread(target=logger) for k in range(10)]
with threading_helper.start_threads(threads):
start.set()
time.sleep(0.1)
stop = True
finally:
sys.setswitchinterval(orig_si)
if __name__ == "__main__":
unittest.main()

View File

@@ -69,6 +69,9 @@ mod sys {
const PS1: &str = ">>>>> ";
#[pyattr(name = "ps2")]
const PS2: &str = "..... ";
#[cfg(windows)]
#[pyattr(name = "_vpath")]
const VPATH: Option<&'static str> = None; // TODO: actual VPATH value
#[pyattr]
fn default_prefix(_vm: &VirtualMachine) -> &'static str {
@@ -684,6 +687,10 @@ mod sys {
dev_mode: bool,
/// -X utf8
utf8_mode: u8,
/// -X int_max_str_digits=number
int_max_str_digits: i8,
/// -P, `PYTHONSAFEPATH`
safe_path: bool,
/// -X warn_default_encoding, PYTHONWARNDEFAULTENCODING
warn_default_encoding: u8,
}
@@ -707,6 +714,8 @@ mod sys {
isolated: settings.isolated as u8,
dev_mode: settings.dev_mode,
utf8_mode: 1,
int_max_str_digits: -1,
safe_path: false,
warn_default_encoding: settings.warn_default_encoding as u8,
}
}
@@ -808,6 +817,8 @@ mod sys {
pub(super) struct PyIntInfo {
bits_per_digit: usize,
sizeof_digit: usize,
default_max_str_digits: usize,
str_digits_check_threshold: usize,
}
#[pyclass(with(PyStructSequence))]
@@ -815,6 +826,8 @@ mod sys {
const INFO: Self = PyIntInfo {
bits_per_digit: 30, //?
sizeof_digit: std::mem::size_of::<u32>(),
default_max_str_digits: 4300,
str_digits_check_threshold: 640,
};
}