Skip to content

Commit 444df49

Browse files
committed
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.
1 parent e56f86f commit 444df49

1 file changed

Lines changed: 25 additions & 30 deletions

File tree

Lib/test/test_embed.py

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
INIT_LOOPS = 4
4949
MAX_HASH_SEED = 4294967295
5050

51+
# Environment variables that change runtime but should not break embedding.
52+
RUNTIME_ENVVARS = ('PYTHON_GIL', 'PYTHON_JIT_STRESS', 'PYTHON_UOPS_OPTIMIZE')
53+
5154
ABI_THREAD = 't' if support.Py_GIL_DISABLED else ''
5255
# PLATSTDLIB_LANDMARK copied from Modules/getpath.py
5356
if os.name == 'nt':
@@ -133,21 +136,24 @@ def tearDown(self):
133136

134137
def run_embedded_interpreter(self, *args, env=None,
135138
timeout=None, returncode=0, input=None,
136-
cwd=None):
139+
cwd=None, runtime_envvars=True):
137140
"""Runs a test in the embedded interpreter"""
138141
cmd = [self.test_exe]
139142
cmd.extend(args)
140-
if env is not None and MS_WINDOWS:
141-
# Windows requires at least the SYSTEMROOT environment variable to
142-
# start Python.
143-
env = env.copy()
144-
env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
143+
safe_env = remove_python_envvars()
144+
# Copy across runtime-modifying variables.
145+
if runtime_envvars:
146+
for name in RUNTIME_ENVVARS:
147+
if name in os.environ:
148+
safe_env[name] = os.environ[name]
149+
if env:
150+
safe_env.update(env)
145151

146152
kwargs = dict(
147153
stdout=subprocess.PIPE,
148154
stderr=subprocess.PIPE,
149155
universal_newlines=True,
150-
env=env,
156+
env=safe_env,
151157
cwd=cwd,
152158
)
153159
if input is not None:
@@ -302,7 +308,7 @@ def test_inittab_submodule_singlephase(self):
302308

303309
def test_forced_io_encoding(self):
304310
# Checks forced configuration of embedded interpreter IO streams
305-
env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape")
311+
env = {'PYTHONIOENCODING': 'utf-8:surrogateescape'}
306312
out, err = self.run_embedded_interpreter("test_forced_io_encoding", env=env)
307313
if support.verbose > 1:
308314
print()
@@ -348,7 +354,7 @@ def test_pre_initialization_api(self):
348354
Checks some key parts of the C-API that need to work before the runtime
349355
is initialized (via Py_Initialize()).
350356
"""
351-
env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
357+
env = {'PYTHONPATH': os.pathsep.join(sys.path)}
352358
out, err = self.run_embedded_interpreter("test_pre_initialization_api", env=env)
353359
if support.verbose > 1:
354360
print()
@@ -369,8 +375,7 @@ def test_pre_initialization_sys_options(self):
369375
Checks that sys.warnoptions and sys._xoptions can be set before the
370376
runtime is initialized (otherwise they won't be effective).
371377
"""
372-
env = remove_python_envvars()
373-
env['PYTHONPATH'] = os.pathsep.join(sys.path)
378+
env = {'PYTHONPATH': os.pathsep.join(sys.path)}
374379
out, err = self.run_embedded_interpreter(
375380
"test_pre_initialization_sys_options", env=env)
376381
if support.verbose > 1:
@@ -628,10 +633,8 @@ def test_init_run_main_startup_exitcode(self):
628633
with open(filename, 'x') as fp:
629634
fp.write(CODE_EXITCODE_123)
630635

631-
env = dict(os.environ)
632-
env['PYTHONSTARTUP'] = filename
633636
self.check_program_exitcode("test_init_run_main_interactive_exitcode",
634-
env=env,
637+
env={'PYTHONSTARTUP': filename},
635638
check_stderr=False)
636639

637640
def test_init_run_main_module_exitcode(self):
@@ -641,10 +644,8 @@ def test_init_run_main_module_exitcode(self):
641644
with open(filename, 'x', encoding='utf8') as fp:
642645
fp.write(CODE_EXITCODE_123)
643646

644-
env = dict(os.environ)
645-
env['PYTHONPATH'] = tmpdir
646647
self.check_program_exitcode("test_init_run_main_module_exitcode",
647-
modname, env=env)
648+
modname, env={'PYTHONPATH': tmpdir})
648649

649650

650651
def config_dev_mode(preconfig, config):
@@ -1035,11 +1036,6 @@ def check_all_configs(self, testname, expected_config=None,
10351036
modify_path_cb=None,
10361037
stderr=None, *, api, preconfig_api=None,
10371038
env=None, ignore_stderr=False, cwd=None):
1038-
new_env = remove_python_envvars()
1039-
if env is not None:
1040-
new_env.update(env)
1041-
env = new_env
1042-
10431039
if preconfig_api is None:
10441040
preconfig_api = api
10451041
if preconfig_api == API_ISOLATED:
@@ -1068,8 +1064,9 @@ def check_all_configs(self, testname, expected_config=None,
10681064
env,
10691065
api, modify_path_cb)
10701066

1071-
out, err = self.run_embedded_interpreter(testname,
1072-
env=env, cwd=cwd)
1067+
# Ignore runtime flags like PYTHON_GIL to get the build configuration.
1068+
out, err = self.run_embedded_interpreter(testname, env=env, cwd=cwd,
1069+
runtime_envvars=False)
10731070
if stderr is None and not expected_config['verbose']:
10741071
stderr = ""
10751072
if stderr is not None and not ignore_stderr:
@@ -1858,8 +1855,7 @@ def test_getpath_abspath_win32(self):
18581855
]
18591856
out, err = self.run_embedded_interpreter(
18601857
"test_init_initialize_config",
1861-
env={**remove_python_envvars(),
1862-
"PYTHONPATH": os.path.pathsep.join(c[0] for c in CASES)}
1858+
env={"PYTHONPATH": os.path.pathsep.join(c[0] for c in CASES)}
18631859
)
18641860
self.assertEqual(err, "")
18651861
try:
@@ -2072,7 +2068,7 @@ def test_audit_run_interactivehook(self):
20722068
print("import sys", file=f)
20732069
print("sys.__interactivehook__ = lambda: None", file=f)
20742070
try:
2075-
env = {**remove_python_envvars(), "PYTHONSTARTUP": startup}
2071+
env = {"PYTHONSTARTUP": startup}
20762072
self.run_embedded_interpreter("test_audit_run_interactivehook",
20772073
timeout=support.SHORT_TIMEOUT,
20782074
returncode=10, env=env)
@@ -2084,7 +2080,7 @@ def test_audit_run_startup(self):
20842080
with open(startup, "w", encoding="utf-8") as f:
20852081
print("pass", file=f)
20862082
try:
2087-
env = {**remove_python_envvars(), "PYTHONSTARTUP": startup}
2083+
env = {"PYTHONSTARTUP": startup}
20882084
self.run_embedded_interpreter("test_audit_run_startup",
20892085
timeout=support.SHORT_TIMEOUT,
20902086
returncode=10, env=env)
@@ -2126,8 +2122,7 @@ def test_unicode_id_init(self):
21262122
'Py_FrozenMain is not exported on Windows')
21272123
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
21282124
def test_frozenmain(self):
2129-
env = dict(os.environ)
2130-
env['PYTHONUNBUFFERED'] = '1'
2125+
env = {'PYTHONUNBUFFERED': '1'}
21312126
out, err = self.run_embedded_interpreter("test_frozenmain", env=env)
21322127
executable = os.path.realpath('./argv0')
21332128
expected = textwrap.dedent(f"""

0 commit comments

Comments
 (0)