https://github.com/python/cpython/commit/9bd44aaa4eae33679c8e2bb6adbc9f2c7b19759f
commit: 9bd44aaa4eae33679c8e2bb6adbc9f2c7b19759f
branch: main
author: Cody Maloney <[email protected]>
committer: cmaloney <[email protected]>
date: 2026-11-03T12:50:49-08:00
summary:
gh-157006: Fix test_embed when PYTHONSTARTUP is set (#157007)
gh-157006: Fix test_embed when PYTHONSTARTUP set
Remove python-specific environment variables in test_embed cases.
Helpers now start with a `PYTHON*` environment variable free copy of
`os.environ` then add back in known runtime changing flags. This makes
it so specific host configuration, such as PYTHON_GIL is kept while
isolating tests from unintended changes. Individual tests which need to
check specific behaviors can pass `env=` to validate embedded
interpreter behavior.
files:
M Lib/test/test_embed.py
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 6c51b79bc44a0f..b5a441b7e94fb4 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -48,6 +48,9 @@
INIT_LOOPS = 4
MAX_HASH_SEED = 4294967295
+# Environment variables that change runtime but should not break embedding.
+RUNTIME_ENVVARS = ('PYTHON_GIL', 'PYTHON_JIT_STRESS', 'PYTHON_UOPS_OPTIMIZE')
+
ABI_THREAD = 't' if support.Py_GIL_DISABLED else ''
# PLATSTDLIB_LANDMARK copied from Modules/getpath.py
if os.name == 'nt':
@@ -133,21 +136,24 @@ def tearDown(self):
def run_embedded_interpreter(self, *args, env=None,
timeout=None, returncode=0, input=None,
- cwd=None):
+ cwd=None, runtime_envvars=True):
"""Runs a test in the embedded interpreter"""
cmd = [self.test_exe]
cmd.extend(args)
- if env is not None and MS_WINDOWS:
- # Windows requires at least the SYSTEMROOT environment variable to
- # start Python.
- env = env.copy()
- env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
+ safe_env = remove_python_envvars()
+ # Copy across runtime-modifying variables.
+ if runtime_envvars:
+ for name in RUNTIME_ENVVARS:
+ if name in os.environ:
+ safe_env[name] = os.environ[name]
+ if env:
+ safe_env.update(env)
kwargs = dict(
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
- env=env,
+ env=safe_env,
cwd=cwd,
)
if input is not None:
@@ -323,7 +329,7 @@ def test_inittab_submodule_singlephase(self):
def test_forced_io_encoding(self):
# Checks forced configuration of embedded interpreter IO streams
- env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape")
+ env = {'PYTHONIOENCODING': 'utf-8:surrogateescape'}
out, err = self.run_embedded_interpreter("test_forced_io_encoding",
env=env)
if support.verbose > 1:
print()
@@ -369,7 +375,7 @@ def test_pre_initialization_api(self):
Checks some key parts of the C-API that need to work before the runtime
is initialized (via Py_Initialize()).
"""
- env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
+ env = {'PYTHONPATH': os.pathsep.join(sys.path)}
out, err =
self.run_embedded_interpreter("test_pre_initialization_api", env=env)
if support.verbose > 1:
print()
@@ -390,8 +396,7 @@ def test_pre_initialization_sys_options(self):
Checks that sys.warnoptions and sys._xoptions can be set before the
runtime is initialized (otherwise they won't be effective).
"""
- env = remove_python_envvars()
- env['PYTHONPATH'] = os.pathsep.join(sys.path)
+ env = {'PYTHONPATH': os.pathsep.join(sys.path)}
out, err = self.run_embedded_interpreter(
"test_pre_initialization_sys_options", env=env)
if support.verbose > 1:
@@ -686,10 +691,8 @@ def test_init_run_main_startup_exitcode(self):
with open(filename, 'x') as fp:
fp.write(CODE_EXITCODE_123)
- env = dict(os.environ)
- env['PYTHONSTARTUP'] = filename
self.check_program_exitcode("test_init_run_main_interactive_exitcode",
- env=env,
+ env={'PYTHONSTARTUP': filename},
check_stderr=False)
def test_init_run_main_module_exitcode(self):
@@ -699,10 +702,8 @@ def test_init_run_main_module_exitcode(self):
with open(filename, 'x', encoding='utf8') as fp:
fp.write(CODE_EXITCODE_123)
- env = dict(os.environ)
- env['PYTHONPATH'] = tmpdir
self.check_program_exitcode("test_init_run_main_module_exitcode",
- modname, env=env)
+ modname, env={'PYTHONPATH': tmpdir})
def config_dev_mode(preconfig, config):
@@ -1061,11 +1062,6 @@ def check_all_configs(self, testname,
expected_config=None,
modify_path_cb=None,
stderr=None, *, api, preconfig_api=None,
env=None, ignore_stderr=False, cwd=None):
- new_env = remove_python_envvars()
- if env is not None:
- new_env.update(env)
- env = new_env
-
if preconfig_api is None:
preconfig_api = api
if preconfig_api == API_ISOLATED:
@@ -1094,8 +1090,9 @@ def check_all_configs(self, testname,
expected_config=None,
env,
api, modify_path_cb)
- out, err = self.run_embedded_interpreter(testname,
- env=env, cwd=cwd)
+ # Ignore runtime flags like PYTHON_GIL to get the build configuration.
+ out, err = self.run_embedded_interpreter(testname, env=env, cwd=cwd,
+ runtime_envvars=False)
if stderr is None and not expected_config['verbose']:
stderr = ""
if stderr is not None and not ignore_stderr:
@@ -1871,8 +1868,7 @@ def test_getpath_abspath_win32(self):
]
out, err = self.run_embedded_interpreter(
"test_init_initialize_config",
- env={**remove_python_envvars(),
- "PYTHONPATH": os.path.pathsep.join(c[0] for c in CASES)}
+ env={"PYTHONPATH": os.path.pathsep.join(c[0] for c in CASES)}
)
self.assertEqual(err, "")
try:
@@ -2085,7 +2081,7 @@ def test_audit_run_interactivehook(self):
print("import sys", file=f)
print("sys.__interactivehook__ = lambda: None", file=f)
try:
- env = {**remove_python_envvars(), "PYTHONSTARTUP": startup}
+ env = {"PYTHONSTARTUP": startup}
self.run_embedded_interpreter("test_audit_run_interactivehook",
timeout=support.SHORT_TIMEOUT,
returncode=10, env=env)
@@ -2097,7 +2093,7 @@ def test_audit_run_startup(self):
with open(startup, "w", encoding="utf-8") as f:
print("pass", file=f)
try:
- env = {**remove_python_envvars(), "PYTHONSTARTUP": startup}
+ env = {"PYTHONSTARTUP": startup}
self.run_embedded_interpreter("test_audit_run_startup",
timeout=support.SHORT_TIMEOUT,
returncode=10, env=env)
@@ -2143,8 +2139,7 @@ def test_unicode_id_init(self):
'Py_FrozenMain is not exported on Windows')
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
def test_frozenmain(self):
- env = dict(os.environ)
- env['PYTHONUNBUFFERED'] = '1'
+ env = {'PYTHONUNBUFFERED': '1'}
out, err = self.run_embedded_interpreter("test_frozenmain", env=env)
executable = os.path.realpath('./argv0')
expected = textwrap.dedent(f"""
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]