Skip to content

Commit 9ee1d42

Browse files
authored
bpo-35233: InitConfigTests tests more config vars (GH-10541) (GH-10546)
test_embed.InitConfigTests tests more configuration variables. Changes: * InitConfigTests tests more core configuration variables: * base_exec_prefix * base_prefix * exec_prefix * home * module_search_path_env * prefix * "_testembed init_from_config" tests more variables: * argv * warnoptions * xoptions * Py_HasFileSystemDefaultEncoding value is no longer tested since it depends on the LC_CTYPE locale and the platform. * InitConfigTests: add check_global_config(), check_core_config() and check_main_config() subfunctions to cleanup the code. Move also constants at the class level (ex: COPY_MAIN_CONFIG). * Use more macros in _PyCoreConfig_AsDict() and _PyMainInterpreterConfig_AsDict() to reduce code duplication. * Other minor cleanups. (cherry picked from commit 01de89c)
1 parent 9053d2f commit 9ee1d42

File tree

4 files changed

+252
-188
lines changed

4 files changed

+252
-188
lines changed

Include/pylifecycle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
8080
PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
8181
_PyMainInterpreterConfig *config,
8282
const _PyMainInterpreterConfig *config2);
83+
/* Used by _testcapi.get_main_config() */
8384
PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
8485
const _PyMainInterpreterConfig *config);
8586

Lib/test/test_embed.py

Lines changed: 136 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
import sys
1111

1212

13+
MS_WINDOWS = (os.name == 'nt')
14+
15+
1316
class EmbeddingTestsMixin:
1417
def setUp(self):
1518
here = os.path.abspath(__file__)
1619
basepath = os.path.dirname(os.path.dirname(os.path.dirname(here)))
1720
exename = "_testembed"
18-
if sys.platform.startswith("win"):
21+
if MS_WINDOWS:
1922
ext = ("_d" if "_d" in sys.executable else "") + ".exe"
2023
exename += ext
2124
exepath = os.path.dirname(sys.executable)
@@ -37,7 +40,7 @@ def run_embedded_interpreter(self, *args, env=None):
3740
"""Runs a test in the embedded interpreter"""
3841
cmd = [self.test_exe]
3942
cmd.extend(args)
40-
if env is not None and sys.platform == 'win32':
43+
if env is not None and MS_WINDOWS:
4144
# Windows requires at least the SYSTEMROOT environment variable to
4245
# start Python.
4346
env = env.copy()
@@ -198,7 +201,7 @@ def test_pre_initialization_api(self):
198201
"""
199202
env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
200203
out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
201-
if sys.platform == "win32":
204+
if MS_WINDOWS:
202205
expected_path = self.test_exe
203206
else:
204207
expected_path = os.path.join(os.getcwd(), "spam")
@@ -252,46 +255,15 @@ def test_initialize_pymain(self):
252255

253256
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
254257
maxDiff = 4096
255-
UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
256-
else 'surrogateescape')
257-
# FIXME: untested core configuration variables
258+
UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape')
259+
260+
# core config
258261
UNTESTED_CORE_CONFIG = (
259-
'base_exec_prefix',
260-
'base_prefix',
261-
'exec_prefix',
262+
# FIXME: untested core configuration variables
263+
'dll_path',
262264
'executable',
263-
'home',
264-
'module_search_path_env',
265265
'module_search_paths',
266-
'prefix',
267-
)
268-
# FIXME: untested main configuration variables
269-
UNTESTED_MAIN_CONFIG = (
270-
'module_search_path',
271266
)
272-
DEFAULT_GLOBAL_CONFIG = {
273-
'Py_BytesWarningFlag': 0,
274-
'Py_DebugFlag': 0,
275-
'Py_DontWriteBytecodeFlag': 0,
276-
'Py_FrozenFlag': 0,
277-
'Py_HasFileSystemDefaultEncoding': 0,
278-
'Py_HashRandomizationFlag': 1,
279-
'Py_InspectFlag': 0,
280-
'Py_InteractiveFlag': 0,
281-
'Py_IsolatedFlag': 0,
282-
'Py_NoSiteFlag': 0,
283-
'Py_NoUserSiteDirectory': 0,
284-
'Py_OptimizeFlag': 0,
285-
'Py_QuietFlag': 0,
286-
'Py_UnbufferedStdioFlag': 0,
287-
'Py_VerboseFlag': 0,
288-
}
289-
if os.name == 'nt':
290-
DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsFSEncodingFlag'] = 0
291-
DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsStdioFlag'] = 0
292-
if sys.platform in ('win32', 'darwin'):
293-
DEFAULT_GLOBAL_CONFIG['Py_HasFileSystemDefaultEncoding'] = 1
294-
295267
DEFAULT_CORE_CONFIG = {
296268
'install_signal_handlers': 1,
297269
'ignore_environment': 0,
@@ -318,9 +290,72 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
318290
'xoptions': [],
319291
'warnoptions': [],
320292

293+
'module_search_path_env': None,
294+
'home': None,
295+
'prefix': sys.prefix,
296+
'base_prefix': sys.base_prefix,
297+
'exec_prefix': sys.exec_prefix,
298+
'base_exec_prefix': sys.base_exec_prefix,
299+
321300
'_disable_importlib': 0,
322301
}
323302

303+
# main config
304+
UNTESTED_MAIN_CONFIG = (
305+
# FIXME: untested main configuration variables
306+
'module_search_path',
307+
)
308+
COPY_MAIN_CONFIG = (
309+
# Copy core config to main config for expected values
310+
'argv',
311+
'base_exec_prefix',
312+
'base_prefix',
313+
'exec_prefix',
314+
'executable',
315+
'install_signal_handlers',
316+
'prefix',
317+
'warnoptions',
318+
# xoptions is created from core_config in check_main_config()
319+
)
320+
321+
# global config
322+
UNTESTED_GLOBAL_CONFIG = (
323+
# Py_HasFileSystemDefaultEncoding value depends on the LC_CTYPE locale
324+
# and the platform. It is complex to test it, and it's value doesn't
325+
# really matter.
326+
'Py_HasFileSystemDefaultEncoding',
327+
)
328+
DEFAULT_GLOBAL_CONFIG = {
329+
'Py_BytesWarningFlag': 0,
330+
'Py_DebugFlag': 0,
331+
'Py_DontWriteBytecodeFlag': 0,
332+
# None means that the value is get by get_filesystem_encoding()
333+
'Py_FileSystemDefaultEncodeErrors': None,
334+
'Py_FileSystemDefaultEncoding': None,
335+
'Py_FrozenFlag': 0,
336+
'Py_HashRandomizationFlag': 1,
337+
'Py_InspectFlag': 0,
338+
'Py_InteractiveFlag': 0,
339+
'Py_IsolatedFlag': 0,
340+
'Py_NoSiteFlag': 0,
341+
'Py_NoUserSiteDirectory': 0,
342+
'Py_OptimizeFlag': 0,
343+
'Py_QuietFlag': 0,
344+
'Py_UnbufferedStdioFlag': 0,
345+
'Py_VerboseFlag': 0,
346+
}
347+
if MS_WINDOWS:
348+
DEFAULT_GLOBAL_CONFIG.update({
349+
'Py_LegacyWindowsFSEncodingFlag': 0,
350+
'Py_LegacyWindowsStdioFlag': 0,
351+
})
352+
COPY_GLOBAL_CONFIG = [
353+
# Copy core config to global config for expected values
354+
# True means that the core config value is inverted (0 => 1 and 1 => 0)
355+
('Py_IgnoreEnvironmentFlag', 'ignore_environment'),
356+
('Py_UTF8Mode', 'utf8_mode'),
357+
]
358+
324359
def get_filesystem_encoding(self, isolated, env):
325360
code = ('import codecs, locale, sys; '
326361
'print(sys.getfilesystemencoding(), '
@@ -339,10 +374,65 @@ def get_filesystem_encoding(self, isolated, env):
339374
out = proc.stdout.rstrip()
340375
return out.split()
341376

342-
def check_config(self, testname, expected, expected_global):
377+
def main_xoptions(self, xoptions_list):
378+
xoptions = {}
379+
for opt in xoptions_list:
380+
if '=' in opt:
381+
key, value = opt.split('=', 1)
382+
xoptions[key] = value
383+
else:
384+
xoptions[opt] = True
385+
return xoptions
386+
387+
def check_main_config(self, config):
388+
core_config = config['core_config']
389+
main_config = config['main_config']
390+
391+
# main config
392+
for key in self.UNTESTED_MAIN_CONFIG:
393+
del main_config[key]
394+
395+
expected_main = {}
396+
for key in self.COPY_MAIN_CONFIG:
397+
expected_main[key] = core_config[key]
398+
expected_main['xoptions'] = self.main_xoptions(core_config['xoptions'])
399+
self.assertEqual(main_config, expected_main)
400+
401+
def check_core_config(self, config, expected):
343402
expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
403+
core_config = dict(config['core_config'])
404+
for key in self.UNTESTED_CORE_CONFIG:
405+
core_config.pop(key, None)
406+
self.assertEqual(core_config, expected)
344407

408+
def check_global_config(self, config, expected, env):
409+
expected = dict(self.DEFAULT_GLOBAL_CONFIG, **expected)
410+
411+
if expected['Py_FileSystemDefaultEncoding'] is None or expected['Py_FileSystemDefaultEncodeErrors'] is None:
412+
res = self.get_filesystem_encoding(expected['Py_IsolatedFlag'], env)
413+
if expected['Py_FileSystemDefaultEncoding'] is None:
414+
expected['Py_FileSystemDefaultEncoding'] = res[0]
415+
if expected['Py_FileSystemDefaultEncodeErrors'] is None:
416+
expected['Py_FileSystemDefaultEncodeErrors'] = res[1]
417+
418+
core_config = config['core_config']
419+
420+
for item in self.COPY_GLOBAL_CONFIG:
421+
if len(item) == 3:
422+
global_key, core_key, opposite = item
423+
expected[global_key] = 0 if core_config[core_key] else 1
424+
else:
425+
global_key, core_key = item
426+
expected[global_key] = core_config[core_key]
427+
428+
global_config = dict(config['global_config'])
429+
for key in self.UNTESTED_GLOBAL_CONFIG:
430+
del global_config[key]
431+
self.assertEqual(global_config, expected)
432+
433+
def check_config(self, testname, expected_core, expected_global):
345434
env = dict(os.environ)
435+
# Remove PYTHON* environment variables to get deterministic environment
346436
for key in list(env):
347437
if key.startswith('PYTHON'):
348438
del env[key]
@@ -353,47 +443,11 @@ def check_config(self, testname, expected, expected_global):
353443

354444
out, err = self.run_embedded_interpreter(testname, env=env)
355445
# Ignore err
356-
357446
config = json.loads(out)
358-
core_config = config['core_config']
359-
executable = core_config['executable']
360-
main_config = config['main_config']
361-
362-
for key in self.UNTESTED_MAIN_CONFIG:
363-
del main_config[key]
364-
365-
expected_main = {
366-
'install_signal_handlers': core_config['install_signal_handlers'],
367-
'argv': [],
368-
'prefix': sys.prefix,
369-
'executable': core_config['executable'],
370-
'base_prefix': sys.base_prefix,
371-
'base_exec_prefix': sys.base_exec_prefix,
372-
'warnoptions': core_config['warnoptions'],
373-
'xoptions': {},
374-
'exec_prefix': core_config['exec_prefix'],
375-
}
376-
self.assertEqual(main_config, expected_main)
377-
378-
expected_global = dict(self.DEFAULT_GLOBAL_CONFIG, **expected_global)
379-
380-
if 'Py_FileSystemDefaultEncoding' not in expected_global:
381-
isolated = expected_global['Py_IsolatedFlag']
382-
fs_encoding, fs_errors = self.get_filesystem_encoding(isolated, env)
383-
expected_global['Py_FileSystemDefaultEncodeErrors'] = fs_errors
384-
expected_global['Py_FileSystemDefaultEncoding'] = fs_encoding
385447

386-
for global_key, core_key in (
387-
('Py_UTF8Mode', 'utf8_mode'),
388-
('Py_IgnoreEnvironmentFlag', 'ignore_environment'),
389-
):
390-
expected_global[global_key] = core_config[core_key]
391-
392-
self.assertEqual(config['global_config'], expected_global)
393-
394-
for key in self.UNTESTED_CORE_CONFIG:
395-
core_config.pop(key, None)
396-
self.assertEqual(core_config, expected)
448+
self.check_core_config(config, expected_core)
449+
self.check_main_config(config)
450+
self.check_global_config(config, expected_global, env)
397451

398452
def test_init_default_config(self):
399453
self.check_config("init_default_config", {}, {})
@@ -406,7 +460,6 @@ def test_init_global_config(self):
406460
global_config = {
407461
'Py_BytesWarningFlag': 1,
408462
'Py_DontWriteBytecodeFlag': 1,
409-
'Py_HasFileSystemDefaultEncoding': 1,
410463
'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
411464
'Py_FileSystemDefaultEncoding': 'utf-8',
412465
'Py_InspectFlag': 1,
@@ -436,12 +489,14 @@ def test_init_from_config(self):
436489
'utf8_mode': 1,
437490

438491
'program_name': './conf_program_name',
492+
'argv': ['-c', 'pass'],
439493
'program': 'conf_program',
494+
'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'],
495+
'warnoptions': ['default', 'error::ResourceWarning'],
440496

441497
'faulthandler': 1,
442498
}
443499
global_config = {
444-
'Py_HasFileSystemDefaultEncoding': 1,
445500
'Py_NoUserSiteDirectory': 0,
446501
}
447502
self.check_config("init_from_config", core_config, global_config)
@@ -460,7 +515,6 @@ def test_init_env(self):
460515
}
461516
global_config = {
462517
'Py_DontWriteBytecodeFlag': 1,
463-
'Py_HasFileSystemDefaultEncoding': 1,
464518
'Py_InspectFlag': 1,
465519
'Py_NoUserSiteDirectory': 1,
466520
'Py_OptimizeFlag': 2,

0 commit comments

Comments
 (0)