Skip to content

Commit 01de89c

Browse files
authored
bpo-35233: InitConfigTests tests more config vars (GH-10541)
test_embed.InitConfigTests tests more configuration variables. Changes: * InitConfigTests tests more core configuration variables: * base_exec_prefix * base_prefix * exec_prefix * home * legacy_windows_fs_encoding * legacy_windows_stdio * module_search_path_env * prefix * "_testembed init_from_config" tests more variables: * argv * warnoptions * xoptions * 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). * Fix _PyCoreConfig_AsDict(): don't set stdio_encoding twice * Use more macros in _PyCoreConfig_AsDict() and _PyMainInterpreterConfig_AsDict() to reduce code duplication. * Other minor cleanups.
1 parent 6431347 commit 01de89c

File tree

6 files changed

+274
-239
lines changed

6 files changed

+274
-239
lines changed

Include/coreconfig.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,9 @@ PyAPI_FUNC(int) _PyCoreConfig_GetEnvDup(
359359
wchar_t *wname,
360360
char *name);
361361

362-
/* Used by _testcapi.get_coreconfig() */
363-
PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
362+
/* Used by _testcapi.get_global_config() and _testcapi.get_core_config() */
364363
PyAPI_FUNC(PyObject *) _Py_GetGlobalVariablesAsDict(void);
364+
PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
365365
#endif
366366

367367
#ifdef __cplusplus

Include/pylifecycle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
3737
PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
3838
_PyMainInterpreterConfig *config,
3939
const _PyMainInterpreterConfig *config2);
40+
/* Used by _testcapi.get_main_config() */
4041
PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
4142
const _PyMainInterpreterConfig *config);
4243

Lib/test/test_embed.py

Lines changed: 127 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
import sys
1212

1313

14+
MS_WINDOWS = (os.name == 'nt')
15+
16+
1417
class EmbeddingTestsMixin:
1518
def setUp(self):
1619
here = os.path.abspath(__file__)
1720
basepath = os.path.dirname(os.path.dirname(os.path.dirname(here)))
1821
exename = "_testembed"
19-
if sys.platform.startswith("win"):
22+
if MS_WINDOWS:
2023
ext = ("_d" if "_d" in sys.executable else "") + ".exe"
2124
exename += ext
2225
exepath = os.path.dirname(sys.executable)
@@ -38,7 +41,7 @@ def run_embedded_interpreter(self, *args, env=None):
3841
"""Runs a test in the embedded interpreter"""
3942
cmd = [self.test_exe]
4043
cmd.extend(args)
41-
if env is not None and sys.platform == 'win32':
44+
if env is not None and MS_WINDOWS:
4245
# Windows requires at least the SYSTEMROOT environment variable to
4346
# start Python.
4447
env = env.copy()
@@ -199,7 +202,7 @@ def test_pre_initialization_api(self):
199202
"""
200203
env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
201204
out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
202-
if sys.platform == "win32":
205+
if MS_WINDOWS:
203206
expected_path = self.test_exe
204207
else:
205208
expected_path = os.path.join(os.getcwd(), "spam")
@@ -253,25 +256,14 @@ def test_initialize_pymain(self):
253256

254257
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
255258
maxDiff = 4096
256-
UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
257-
else 'surrogateescape')
258-
# FIXME: untested core configuration variables
259+
UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape')
260+
261+
# core config
259262
UNTESTED_CORE_CONFIG = (
260-
'base_exec_prefix',
261-
'base_prefix',
263+
# FIXME: untested core configuration variables
262264
'dll_path',
263-
'exec_prefix',
264265
'executable',
265-
'home',
266-
'legacy_windows_fs_encoding',
267-
'legacy_windows_stdio',
268-
'module_search_path_env',
269266
'module_search_paths',
270-
'prefix',
271-
)
272-
# FIXME: untested main configuration variables
273-
UNTESTED_MAIN_CONFIG = (
274-
'module_search_path',
275267
)
276268
DEFAULT_CORE_CONFIG = {
277269
'install_signal_handlers': 1,
@@ -304,6 +296,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
304296
'xoptions': [],
305297
'warnoptions': [],
306298

299+
'module_search_path_env': None,
300+
'home': None,
301+
'prefix': sys.prefix,
302+
'base_prefix': sys.base_prefix,
303+
'exec_prefix': sys.exec_prefix,
304+
'base_exec_prefix': sys.base_exec_prefix,
305+
307306
'isolated': 0,
308307
'site_import': 1,
309308
'bytes_warning': 0,
@@ -325,6 +324,63 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
325324
'_check_hash_pycs_mode': 'default',
326325
'_frozen': 0,
327326
}
327+
if MS_WINDOWS:
328+
DEFAULT_CORE_CONFIG.update({
329+
'legacy_windows_fs_encoding': 0,
330+
'legacy_windows_stdio': 0,
331+
})
332+
333+
# main config
334+
UNTESTED_MAIN_CONFIG = (
335+
# FIXME: untested main configuration variables
336+
'module_search_path',
337+
)
338+
COPY_MAIN_CONFIG = (
339+
# Copy core config to main config for expected values
340+
'argv',
341+
'base_exec_prefix',
342+
'base_prefix',
343+
'exec_prefix',
344+
'executable',
345+
'install_signal_handlers',
346+
'prefix',
347+
'pycache_prefix',
348+
'warnoptions',
349+
# xoptions is created from core_config in check_main_config()
350+
)
351+
352+
# global config
353+
DEFAULT_GLOBAL_CONFIG = {
354+
'Py_HasFileSystemDefaultEncoding': 0,
355+
'Py_HashRandomizationFlag': 1,
356+
'_Py_HasFileSystemDefaultEncodeErrors': 0,
357+
}
358+
COPY_GLOBAL_CONFIG = [
359+
# Copy core config to global config for expected values
360+
# True means that the core config value is inverted (0 => 1 and 1 => 0)
361+
('Py_BytesWarningFlag', 'bytes_warning'),
362+
('Py_DebugFlag', 'parser_debug'),
363+
('Py_DontWriteBytecodeFlag', 'write_bytecode', True),
364+
('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
365+
('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
366+
('Py_FrozenFlag', '_frozen'),
367+
('Py_IgnoreEnvironmentFlag', 'use_environment', True),
368+
('Py_InspectFlag', 'inspect'),
369+
('Py_InteractiveFlag', 'interactive'),
370+
('Py_IsolatedFlag', 'isolated'),
371+
('Py_NoSiteFlag', 'site_import', True),
372+
('Py_NoUserSiteDirectory', 'user_site_directory', True),
373+
('Py_OptimizeFlag', 'optimization_level'),
374+
('Py_QuietFlag', 'quiet'),
375+
('Py_UTF8Mode', 'utf8_mode'),
376+
('Py_UnbufferedStdioFlag', 'buffered_stdio', True),
377+
('Py_VerboseFlag', 'verbose'),
378+
]
379+
if MS_WINDOWS:
380+
COPY_GLOBAL_CONFIG.extend((
381+
('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'),
382+
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
383+
))
328384

329385
def get_stdio_encoding(self, env):
330386
code = 'import sys; print(sys.stdout.encoding, sys.stdout.errors)'
@@ -355,18 +411,31 @@ def get_filesystem_encoding(self, isolated, env):
355411
out = proc.stdout.rstrip()
356412
return out.split()
357413

358-
def check_config(self, testname, expected):
359-
expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
414+
def main_xoptions(self, xoptions_list):
415+
xoptions = {}
416+
for opt in xoptions_list:
417+
if '=' in opt:
418+
key, value = opt.split('=', 1)
419+
xoptions[key] = value
420+
else:
421+
xoptions[opt] = True
422+
return xoptions
360423

361-
env = dict(os.environ)
362-
for key in list(env):
363-
if key.startswith('PYTHON'):
364-
del env[key]
365-
# Disable C locale coercion and UTF-8 mode to not depend
366-
# on the current locale
367-
env['PYTHONCOERCECLOCALE'] = '0'
368-
env['PYTHONUTF8'] = '0'
424+
def check_main_config(self, config):
425+
core_config = config['core_config']
426+
main_config = config['main_config']
427+
428+
# main config
429+
for key in self.UNTESTED_MAIN_CONFIG:
430+
del main_config[key]
369431

432+
expected_main = {}
433+
for key in self.COPY_MAIN_CONFIG:
434+
expected_main[key] = core_config[key]
435+
expected_main['xoptions'] = self.main_xoptions(core_config['xoptions'])
436+
self.assertEqual(main_config, expected_main)
437+
438+
def check_core_config(self, config, expected, env):
370439
if expected['stdio_encoding'] is None or expected['stdio_errors'] is None:
371440
res = self.get_stdio_encoding(env)
372441
if expected['stdio_encoding'] is None:
@@ -380,74 +449,45 @@ def check_config(self, testname, expected):
380449
if expected['filesystem_errors'] is None:
381450
expected['filesystem_errors'] = res[1]
382451

383-
out, err = self.run_embedded_interpreter(testname, env=env)
384-
# Ignore err
452+
core_config = dict(config['core_config'])
453+
for key in self.UNTESTED_CORE_CONFIG:
454+
core_config.pop(key, None)
455+
self.assertEqual(core_config, expected)
385456

386-
config = json.loads(out)
457+
def check_global_config(self, config):
387458
core_config = config['core_config']
388-
executable = core_config['executable']
389-
main_config = config['main_config']
390459

391-
for key in self.UNTESTED_MAIN_CONFIG:
392-
del main_config[key]
393-
394-
expected_main = {
395-
'install_signal_handlers': core_config['install_signal_handlers'],
396-
'argv': [],
397-
'prefix': sys.prefix,
398-
'executable': core_config['executable'],
399-
'base_prefix': sys.base_prefix,
400-
'base_exec_prefix': sys.base_exec_prefix,
401-
'warnoptions': core_config['warnoptions'],
402-
'xoptions': {},
403-
'pycache_prefix': core_config['pycache_prefix'],
404-
'exec_prefix': core_config['exec_prefix'],
405-
}
406-
self.assertEqual(main_config, expected_main)
407-
408-
409-
copy_global_config = [
410-
('Py_BytesWarningFlag', 'bytes_warning'),
411-
('Py_DebugFlag', 'parser_debug'),
412-
('Py_DontWriteBytecodeFlag', 'write_bytecode', True),
413-
('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
414-
('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
415-
('Py_FrozenFlag', '_frozen'),
416-
('Py_IgnoreEnvironmentFlag', 'use_environment', True),
417-
('Py_InspectFlag', 'inspect'),
418-
('Py_InteractiveFlag', 'interactive'),
419-
('Py_IsolatedFlag', 'isolated'),
420-
('Py_NoSiteFlag', 'site_import', True),
421-
('Py_NoUserSiteDirectory', 'user_site_directory', True),
422-
('Py_OptimizeFlag', 'optimization_level'),
423-
('Py_QuietFlag', 'quiet'),
424-
('Py_UTF8Mode', 'utf8_mode'),
425-
('Py_UnbufferedStdioFlag', 'buffered_stdio', True),
426-
('Py_VerboseFlag', 'verbose'),
427-
]
428-
if os.name == 'nt':
429-
copy_global_config.extend((
430-
('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'),
431-
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
432-
))
433-
434-
expected_global = {}
435-
for item in copy_global_config:
460+
expected_global = dict(self.DEFAULT_GLOBAL_CONFIG)
461+
for item in self.COPY_GLOBAL_CONFIG:
436462
if len(item) == 3:
437463
global_key, core_key, opposite = item
438464
expected_global[global_key] = 0 if core_config[core_key] else 1
439465
else:
440466
global_key, core_key = item
441467
expected_global[global_key] = core_config[core_key]
442468

443-
expected_global['Py_HasFileSystemDefaultEncoding'] = 0
444-
expected_global['_Py_HasFileSystemDefaultEncodeErrors'] = 0
445-
expected_global['Py_HashRandomizationFlag'] = 1
446469
self.assertEqual(config['global_config'], expected_global)
447470

448-
for key in self.UNTESTED_CORE_CONFIG:
449-
core_config.pop(key, None)
450-
self.assertEqual(core_config, expected)
471+
def check_config(self, testname, expected):
472+
expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
473+
474+
env = dict(os.environ)
475+
# Remove PYTHON* environment variables to get deterministic environment
476+
for key in list(env):
477+
if key.startswith('PYTHON'):
478+
del env[key]
479+
# Disable C locale coercion and UTF-8 mode to not depend
480+
# on the current locale
481+
env['PYTHONCOERCECLOCALE'] = '0'
482+
env['PYTHONUTF8'] = '0'
483+
484+
out, err = self.run_embedded_interpreter(testname, env=env)
485+
# Ignore err
486+
config = json.loads(out)
487+
488+
self.check_core_config(config, expected, env)
489+
self.check_main_config(config)
490+
self.check_global_config(config)
451491

452492
def test_init_default_config(self):
453493
self.check_config("init_default_config", {})
@@ -495,7 +535,10 @@ def test_init_from_config(self):
495535

496536
'pycache_prefix': 'conf_pycache_prefix',
497537
'program_name': './conf_program_name',
538+
'argv': ['-c', 'pass'],
498539
'program': 'conf_program',
540+
'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'],
541+
'warnoptions': ['default', 'error::ResourceWarning'],
499542

500543
'site_import': 0,
501544
'bytes_warning': 1,

0 commit comments

Comments
 (0)