Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Lib/test/.ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extend = "../../.ruff.toml" # Inherit the project-wide settings

target-version = "py312"

extend-exclude = [
# Excluded (run with the other AC files in its own separate ruff job in pre-commit)
"test_clinic.py",
# Excluded (these aren't actually executed, they're just "data files")
"tokenizedata/*.py",
# Non UTF-8 files
"encoded_modules/module_iso_8859_1.py",
"encoded_modules/module_koi8_r.py",
# New grammar constructions may not yet be recognized by Ruff,
# and tests re-use the same names as only the grammar is being checked.
"test_grammar.py",
]

[per-file-target-version]
# Type parameter defaults
"test_type_params.py" = "py313"

# Template string literals
"test_annotationlib.py" = "py314"
"test_string/test_templatelib.py" = "py314"
"test_tstring.py" = "py314"

[lint]
select = [
"F811", # Redefinition of unused variable (useful for finding test methods with the same name)
]
291 changes: 291 additions & 0 deletions Lib/test/_test_embed_set_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
# bpo-42260: Test _PyInterpreterState_GetConfigCopy()
# and _PyInterpreterState_SetConfig().
#
# Test run in a subprocess since set_config(get_config())
# does reset sys attributes to their state of the Python startup
# (before the site module is run).

import _testinternalcapi
import os
import sys
import unittest
from test import support
from test.support import MS_WINDOWS


MAX_HASH_SEED = 4294967295


BOOL_OPTIONS = [
'isolated',
'use_environment',
'dev_mode',
'install_signal_handlers',
'use_hash_seed',
'faulthandler',
'import_time',
'code_debug_ranges',
'show_ref_count',
'dump_refs',
'malloc_stats',
'parse_argv',
'site_import',
'warn_default_encoding',
'inspect',
'interactive',
'parser_debug',
'write_bytecode',
'quiet',
'user_site_directory',
'configure_c_stdio',
'buffered_stdio',
'use_frozen_modules',
'safe_path',
'pathconfig_warnings',
'module_search_paths_set',
'skip_source_first_line',
'_install_importlib',
'_init_main',
'_is_python_build',
]
if MS_WINDOWS:
BOOL_OPTIONS.append('legacy_windows_stdio')


class SetConfigTests(unittest.TestCase):
def setUp(self):
self.old_config = _testinternalcapi.get_config()
self.sys_copy = dict(sys.__dict__)

def tearDown(self):
_testinternalcapi.reset_path_config()
_testinternalcapi.set_config(self.old_config)
sys.__dict__.clear()
sys.__dict__.update(self.sys_copy)

def set_config(self, **kwargs):
_testinternalcapi.set_config(self.old_config | kwargs)

def check(self, **kwargs):
self.set_config(**kwargs)
for key, value in kwargs.items():
self.assertEqual(getattr(sys, key), value,
(key, value))

def test_set_invalid(self):
invalid_uint = -1
NULL = None
invalid_wstr = NULL
# PyWideStringList strings must be non-NULL
invalid_wstrlist = ["abc", NULL, "def"]

type_tests = []
value_tests = [
# enum
('_config_init', 0),
('_config_init', 4),
# unsigned long
("hash_seed", -1),
("hash_seed", MAX_HASH_SEED + 1),
]

# int (unsigned)
int_options = [
'_config_init',
'bytes_warning',
'optimization_level',
'tracemalloc',
'verbose',
]
int_options.extend(BOOL_OPTIONS)
for key in int_options:
value_tests.append((key, invalid_uint))
type_tests.append((key, "abc"))
type_tests.append((key, 2.0))

# wchar_t*
for key in (
'filesystem_encoding',
'filesystem_errors',
'stdio_encoding',
'stdio_errors',
'check_hash_pycs_mode',
'program_name',
'platlibdir',
# optional wstr:
# 'pythonpath_env'
# 'home'
# 'pycache_prefix'
# 'run_command'
# 'run_module'
# 'run_filename'
# 'executable'
# 'prefix'
# 'exec_prefix'
# 'base_executable'
# 'base_prefix'
# 'base_exec_prefix'
):
value_tests.append((key, invalid_wstr))
type_tests.append((key, b'bytes'))
type_tests.append((key, 123))

# PyWideStringList
for key in (
'orig_argv',
'argv',
'xoptions',
'warnoptions',
'module_search_paths',
):
value_tests.append((key, invalid_wstrlist))
type_tests.append((key, 123))
type_tests.append((key, "abc"))
type_tests.append((key, [123]))
type_tests.append((key, [b"bytes"]))


if MS_WINDOWS:
value_tests.append(('legacy_windows_stdio', invalid_uint))

for exc_type, tests in (
(ValueError, value_tests),
(TypeError, type_tests),
):
for key, value in tests:
config = self.old_config | {key: value}
with self.subTest(key=key, value=value, exc_type=exc_type):
with self.assertRaises(exc_type):
_testinternalcapi.set_config(config)

def test_flags(self):
bool_options = set(BOOL_OPTIONS)
for sys_attr, key, value in (
("debug", "parser_debug", 1),
("inspect", "inspect", 2),
("interactive", "interactive", 3),
("optimize", "optimization_level", 4),
("verbose", "verbose", 1),
("bytes_warning", "bytes_warning", 10),
("quiet", "quiet", 11),
("isolated", "isolated", 12),
):
with self.subTest(sys=sys_attr, key=key, value=value):
self.set_config(**{key: value, 'parse_argv': 0})
if key in bool_options:
self.assertEqual(getattr(sys.flags, sys_attr), int(bool(value)))
else:
self.assertEqual(getattr(sys.flags, sys_attr), value)

self.set_config(write_bytecode=0)
self.assertEqual(sys.flags.dont_write_bytecode, True)
self.assertEqual(sys.dont_write_bytecode, True)

self.set_config(write_bytecode=1)
self.assertEqual(sys.flags.dont_write_bytecode, False)
self.assertEqual(sys.dont_write_bytecode, False)

self.set_config(user_site_directory=0, isolated=0)
self.assertEqual(sys.flags.no_user_site, 1)
self.set_config(user_site_directory=1, isolated=0)
self.assertEqual(sys.flags.no_user_site, 0)

self.set_config(site_import=0)
self.assertEqual(sys.flags.no_site, 1)
self.set_config(site_import=1)
self.assertEqual(sys.flags.no_site, 0)

self.set_config(dev_mode=0)
self.assertEqual(sys.flags.dev_mode, False)
self.set_config(dev_mode=1)
self.assertEqual(sys.flags.dev_mode, True)

self.set_config(use_environment=0, isolated=0)
self.assertEqual(sys.flags.ignore_environment, 1)
self.set_config(use_environment=1, isolated=0)
self.assertEqual(sys.flags.ignore_environment, 0)

self.set_config(use_hash_seed=1, hash_seed=0)
self.assertEqual(sys.flags.hash_randomization, 0)
self.set_config(use_hash_seed=0, hash_seed=0)
self.assertEqual(sys.flags.hash_randomization, 1)
self.set_config(use_hash_seed=1, hash_seed=123)
self.assertEqual(sys.flags.hash_randomization, 1)

if support.Py_GIL_DISABLED:
self.set_config(enable_gil=-1)
self.assertEqual(sys.flags.gil, None)
self.set_config(enable_gil=0)
self.assertEqual(sys.flags.gil, 0)
self.set_config(enable_gil=1)
self.assertEqual(sys.flags.gil, 1)
else:
# Builds without Py_GIL_DISABLED don't have
# PyConfig.enable_gil. sys.flags.gil is always defined to 1, for
# consistency.
self.assertEqual(sys.flags.gil, 1)

def test_options(self):
self.check(warnoptions=[])
self.check(warnoptions=["default", "ignore"])

self.set_config(xoptions=[])
self.assertEqual(sys._xoptions, {})
self.set_config(xoptions=["dev", "tracemalloc=5"])
self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"})

def test_pathconfig(self):
self.check(
executable='executable',
prefix="prefix",
base_prefix="base_prefix",
exec_prefix="exec_prefix",
base_exec_prefix="base_exec_prefix",
platlibdir="platlibdir")

self.set_config(base_executable="base_executable")
self.assertEqual(sys._base_executable, "base_executable")

# When base_xxx is NULL, value is copied from xxxx
self.set_config(
executable='executable',
prefix="prefix",
exec_prefix="exec_prefix",
base_executable=None,
base_prefix=None,
base_exec_prefix=None)
self.assertEqual(sys._base_executable, "executable")
self.assertEqual(sys.base_prefix, "prefix")
self.assertEqual(sys.base_exec_prefix, "exec_prefix")

def test_path(self):
self.set_config(module_search_paths_set=1,
module_search_paths=['a', 'b', 'c'])
self.assertEqual(sys.path, ['a', 'b', 'c'])

# sys.path is reset if module_search_paths_set=0
self.set_config(module_search_paths_set=0,
module_search_paths=['new_path'])
self.assertNotEqual(sys.path, ['a', 'b', 'c'])
self.assertNotEqual(sys.path, ['new_path'])

def test_argv(self):
self.set_config(parse_argv=0,
argv=['python_program', 'args'],
orig_argv=['orig', 'orig_args'])
self.assertEqual(sys.argv, ['python_program', 'args'])
self.assertEqual(sys.orig_argv, ['orig', 'orig_args'])

self.set_config(parse_argv=0,
argv=[],
orig_argv=[])
self.assertEqual(sys.argv, [''])
self.assertEqual(sys.orig_argv, [])

def test_pycache_prefix(self):
self.check(pycache_prefix=None)
self.check(pycache_prefix="pycache_prefix")


if __name__ == "__main__":
unittest.main()
59 changes: 59 additions & 0 deletions Lib/test/_test_embed_structseq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import sys
import types

# Note: This test file can't import `unittest` since the runtime can't
# currently guarantee that it will not leak memory. Doing so will mark
# the test as passing but with reference leaks. This can safely import
# the `unittest` library once there's a strict guarantee of no leaks
# during runtime shutdown.

# bpo-46417: Test that structseq types used by the sys module are still
# valid when Py_Finalize()/Py_Initialize() are called multiple times.
class TestStructSeq:
# test PyTypeObject members
def _check_structseq(self, obj_type):
# ob_refcnt
assert sys.getrefcount(obj_type) > 1
# tp_base
assert issubclass(obj_type, tuple)
# tp_bases
assert obj_type.__bases__ == (tuple,)
# tp_dict
assert isinstance(obj_type.__dict__, types.MappingProxyType)
# tp_mro
assert obj_type.__mro__ == (obj_type, tuple, object)
# tp_name
assert isinstance(type.__name__, str)
# tp_subclasses
assert obj_type.__subclasses__() == []

def test_sys_attrs(self):
for attr_name in (
'flags', # FlagsType
'float_info', # FloatInfoType
'hash_info', # Hash_InfoType
'int_info', # Int_InfoType
'thread_info', # ThreadInfoType
'version_info', # VersionInfoType
):
attr = getattr(sys, attr_name)
self._check_structseq(type(attr))

def test_sys_funcs(self):
func_names = ['get_asyncgen_hooks'] # AsyncGenHooksType
if hasattr(sys, 'getwindowsversion'):
func_names.append('getwindowsversion') # WindowsVersionType
for func_name in func_names:
func = getattr(sys, func_name)
obj = func()
self._check_structseq(type(obj))


try:
tests = TestStructSeq()
tests.test_sys_attrs()
tests.test_sys_funcs()
except SystemExit as exc:
if exc.args[0] != 0:
raise
print("Tests passed")
Loading
Loading