Skip to content

Commit 1075d16

Browse files
authored
bpo-36301: Add _Py_GetConfigsAsDict() function (GH-12540)
* Add _Py_GetConfigsAsDict() function to get all configurations as a dict. * dump_config() of _testembed.c now dumps preconfig as a separated key: call _Py_GetConfigsAsDict(). * Make _PyMainInterpreterConfig_AsDict() private.
1 parent 91759d9 commit 1075d16

File tree

9 files changed

+177
-132
lines changed

9 files changed

+177
-132
lines changed

Include/cpython/coreconfig.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,7 @@ typedef struct {
400400

401401
/* --- Function used for testing ---------------------------------- */
402402

403-
PyAPI_FUNC(PyObject *) _Py_GetGlobalVariablesAsDict(void);
404-
PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
403+
PyAPI_FUNC(PyObject*) _Py_GetConfigsAsDict(void);
405404

406405
#ifdef __cplusplus
407406
}

Include/cpython/pylifecycle.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
3333
PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
3434
_PyMainInterpreterConfig *config,
3535
const _PyMainInterpreterConfig *config2);
36-
/* Used by _testcapi.get_main_config() */
37-
PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
38-
const _PyMainInterpreterConfig *config);
3936

4037
PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
4138
PyInterpreterState *interp,

Include/internal/pycore_coreconfig.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ PyAPI_FUNC(void) _Py_get_env_flag(_PyPreConfig *config,
9090
PyAPI_FUNC(_PyInitError) _PyPreConfig_Read(_PyPreConfig *config,
9191
const _PyArgv *args,
9292
const _PyCoreConfig *coreconfig);
93-
PyAPI_FUNC(int) _PyPreConfig_AsDict(const _PyPreConfig *config,
94-
PyObject *dict);
93+
PyAPI_FUNC(PyObject*) _PyPreConfig_AsDict(const _PyPreConfig *config);
9594
PyAPI_FUNC(_PyInitError) _PyPreConfig_ReadFromArgv(_PyPreConfig *config,
9695
const _PyArgv *args);
9796
PyAPI_FUNC(_PyInitError) _PyPreConfig_Write(_PyPreConfig *config);
@@ -121,6 +120,11 @@ PyAPI_FUNC(_PyInitError) _PyCoreConfig_ReadFromArgv(_PyCoreConfig *config,
121120
const _PyArgv *args);
122121
PyAPI_FUNC(_PyInitError) _PyCoreConfig_Write(const _PyCoreConfig *config);
123122

123+
/* --- _PyMainInterpreterConfig ----------------------------------- */
124+
125+
PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
126+
const _PyMainInterpreterConfig *config);
127+
124128
#ifdef __cplusplus
125129
}
126130
#endif

Lib/test/pythoninfo.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -598,18 +598,15 @@ def collect_get_config(info_add):
598598
# Dump global configuration variables, _PyCoreConfig
599599
# and _PyMainInterpreterConfig
600600
try:
601-
from _testcapi import get_global_config, get_core_config, get_main_config
601+
from _testcapi import get_configs
602602
except ImportError:
603603
return
604604

605-
for prefix, get_config_func in (
606-
('global_config', get_global_config),
607-
('core_config', get_core_config),
608-
('main_config', get_main_config),
609-
):
610-
config = get_config_func()
605+
all_configs = get_configs()
606+
for config_type in sorted(all_configs):
607+
config = all_configs[config_type]
611608
for key in sorted(config):
612-
info_add('%s[%s]' % (prefix, key), repr(config[key]))
609+
info_add('%s[%s]' % (config_type, key), repr(config[key]))
613610

614611

615612
def collect_subprocess(info_add):

Lib/test/test_embed.py

Lines changed: 74 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,19 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
268268
)
269269
# Mark config which should be get by get_default_config()
270270
GET_DEFAULT_CONFIG = object()
271+
DEFAULT_PRE_CONFIG = {
272+
'allocator': None,
273+
'coerce_c_locale': 0,
274+
'coerce_c_locale_warn': 0,
275+
'dev_mode': 0,
276+
'isolated': 0,
277+
'use_environment': 1,
278+
'utf8_mode': 0,
279+
}
271280
DEFAULT_CORE_CONFIG = {
272281
'install_signal_handlers': 1,
273-
'use_environment': 1,
274282
'use_hash_seed': 0,
275283
'hash_seed': 0,
276-
'allocator': None,
277-
'dev_mode': 0,
278284
'faulthandler': 0,
279285
'tracemalloc': 0,
280286
'import_time': 0,
@@ -286,10 +292,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
286292
'filesystem_encoding': GET_DEFAULT_CONFIG,
287293
'filesystem_errors': GET_DEFAULT_CONFIG,
288294

289-
'utf8_mode': 0,
290-
'coerce_c_locale': 0,
291-
'coerce_c_locale_warn': 0,
292-
293295
'pycache_prefix': None,
294296
'program_name': './_testembed',
295297
'argv': [""],
@@ -306,7 +308,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
306308
'exec_prefix': GET_DEFAULT_CONFIG,
307309
'base_exec_prefix': GET_DEFAULT_CONFIG,
308310

309-
'isolated': 0,
310311
'site_import': 1,
311312
'bytes_warning': 0,
312313
'inspect': 0,
@@ -332,8 +333,10 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
332333
'_frozen': 0,
333334
}
334335
if MS_WINDOWS:
335-
DEFAULT_CORE_CONFIG.update({
336+
DEFAULT_PRE_CONFIG.update({
336337
'legacy_windows_fs_encoding': 0,
338+
})
339+
DEFAULT_CORE_CONFIG.update({
337340
'legacy_windows_stdio': 0,
338341
})
339342

@@ -359,6 +362,11 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
359362
'Py_HashRandomizationFlag': 1,
360363
'_Py_HasFileSystemDefaultEncodeErrors': 0,
361364
}
365+
COPY_GLOBAL_PRE_CONFIG = [
366+
('Py_IgnoreEnvironmentFlag', 'use_environment', True),
367+
('Py_IsolatedFlag', 'isolated'),
368+
('Py_UTF8Mode', 'utf8_mode'),
369+
]
362370
COPY_GLOBAL_CONFIG = [
363371
# Copy core config to global config for expected values
364372
# True means that the core config value is inverted (0 => 1 and 1 => 0)
@@ -368,21 +376,20 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
368376
('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
369377
('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
370378
('Py_FrozenFlag', '_frozen'),
371-
('Py_IgnoreEnvironmentFlag', 'use_environment', True),
372379
('Py_InspectFlag', 'inspect'),
373380
('Py_InteractiveFlag', 'interactive'),
374-
('Py_IsolatedFlag', 'isolated'),
375381
('Py_NoSiteFlag', 'site_import', True),
376382
('Py_NoUserSiteDirectory', 'user_site_directory', True),
377383
('Py_OptimizeFlag', 'optimization_level'),
378384
('Py_QuietFlag', 'quiet'),
379-
('Py_UTF8Mode', 'utf8_mode'),
380385
('Py_UnbufferedStdioFlag', 'buffered_stdio', True),
381386
('Py_VerboseFlag', 'verbose'),
382387
]
383388
if MS_WINDOWS:
384-
COPY_GLOBAL_CONFIG.extend((
389+
COPY_GLOBAL_PRE_CONFIG.extend((
385390
('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'),
391+
))
392+
COPY_GLOBAL_CONFIG.extend((
386393
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
387394
))
388395

@@ -408,7 +415,7 @@ def check_main_config(self, config):
408415
expected['xoptions'] = self.main_xoptions(core_config['xoptions'])
409416
self.assertEqual(main_config, expected)
410417

411-
def get_expected_config(self, expected, env):
418+
def get_expected_config(self, expected, expected_preconfig, env):
412419
expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
413420

414421
code = textwrap.dedent('''
@@ -436,7 +443,7 @@ def get_expected_config(self, expected, env):
436443
# when test_embed is run from a venv (bpo-35313)
437444
args = (sys.executable, '-S', '-c', code)
438445
env = dict(env)
439-
if not expected['isolated']:
446+
if not expected_preconfig['isolated']:
440447
env['PYTHONCOERCECLOCALE'] = '0'
441448
env['PYTHONUTF8'] = '0'
442449
proc = subprocess.run(args, env=env,
@@ -453,13 +460,19 @@ def get_expected_config(self, expected, env):
453460
expected[key] = config[key]
454461
return expected
455462

463+
def check_pre_config(self, config, expected):
464+
pre_config = dict(config['pre_config'])
465+
core_config = dict(config['core_config'])
466+
self.assertEqual(pre_config, expected)
467+
456468
def check_core_config(self, config, expected):
457469
core_config = dict(config['core_config'])
458470
for key in self.UNTESTED_CORE_CONFIG:
459471
core_config.pop(key, None)
460472
self.assertEqual(core_config, expected)
461473

462474
def check_global_config(self, config):
475+
pre_config = config['pre_config']
463476
core_config = config['core_config']
464477

465478
expected = dict(self.DEFAULT_GLOBAL_CONFIG)
@@ -470,10 +483,17 @@ def check_global_config(self, config):
470483
else:
471484
global_key, core_key = item
472485
expected[global_key] = core_config[core_key]
486+
for item in self.COPY_GLOBAL_PRE_CONFIG:
487+
if len(item) == 3:
488+
global_key, core_key, opposite = item
489+
expected[global_key] = 0 if pre_config[core_key] else 1
490+
else:
491+
global_key, core_key = item
492+
expected[global_key] = pre_config[core_key]
473493

474494
self.assertEqual(config['global_config'], expected)
475495

476-
def check_config(self, testname, expected):
496+
def check_config(self, testname, expected_config, expected_preconfig):
477497
env = dict(os.environ)
478498
# Remove PYTHON* environment variables to get deterministic environment
479499
for key in list(env):
@@ -488,15 +508,21 @@ def check_config(self, testname, expected):
488508
# Ignore err
489509
config = json.loads(out)
490510

491-
expected = self.get_expected_config(expected, env)
492-
self.check_core_config(config, expected)
511+
expected_preconfig = dict(self.DEFAULT_PRE_CONFIG, **expected_preconfig)
512+
expected_config = self.get_expected_config(expected_config, expected_preconfig, env)
513+
514+
self.check_core_config(config, expected_config)
515+
self.check_pre_config(config, expected_preconfig)
493516
self.check_main_config(config)
494517
self.check_global_config(config)
495518

496519
def test_init_default_config(self):
497-
self.check_config("init_default_config", {})
520+
self.check_config("init_default_config", {}, {})
498521

499522
def test_init_global_config(self):
523+
preconfig = {
524+
'utf8_mode': 1,
525+
}
500526
config = {
501527
'program_name': './globalvar',
502528
'site_import': 0,
@@ -509,29 +535,30 @@ def test_init_global_config(self):
509535
'quiet': 1,
510536
'buffered_stdio': 0,
511537

512-
'utf8_mode': 1,
513538
'stdio_encoding': 'utf-8',
514539
'stdio_errors': 'surrogateescape',
515540
'filesystem_encoding': 'utf-8',
516541
'filesystem_errors': self.UTF8_MODE_ERRORS,
517542
'user_site_directory': 0,
518543
'_frozen': 1,
519544
}
520-
self.check_config("init_global_config", config)
545+
self.check_config("init_global_config", config, preconfig)
521546

522547
def test_init_from_config(self):
548+
preconfig = {
549+
'allocator': 'malloc',
550+
'utf8_mode': 1,
551+
}
523552
config = {
524553
'install_signal_handlers': 0,
525554
'use_hash_seed': 1,
526555
'hash_seed': 123,
527-
'allocator': 'malloc',
528556
'tracemalloc': 2,
529557
'import_time': 1,
530558
'show_ref_count': 1,
531559
'show_alloc_count': 1,
532560
'malloc_stats': 1,
533561

534-
'utf8_mode': 1,
535562
'stdio_encoding': 'iso8859-1',
536563
'stdio_errors': 'replace',
537564
'filesystem_encoding': 'utf-8',
@@ -559,16 +586,18 @@ def test_init_from_config(self):
559586
'_check_hash_pycs_mode': 'always',
560587
'_frozen': 1,
561588
}
562-
self.check_config("init_from_config", config)
589+
self.check_config("init_from_config", config, preconfig)
563590

591+
INIT_ENV_PRECONFIG = {
592+
'allocator': 'malloc',
593+
'utf8_mode': 1,
594+
}
564595
INIT_ENV_CONFIG = {
565596
'use_hash_seed': 1,
566597
'hash_seed': 42,
567-
'allocator': 'malloc',
568598
'tracemalloc': 2,
569599
'import_time': 1,
570600
'malloc_stats': 1,
571-
'utf8_mode': 1,
572601
'filesystem_encoding': 'utf-8',
573602
'filesystem_errors': UTF8_MODE_ERRORS,
574603
'inspect': 1,
@@ -584,35 +613,42 @@ def test_init_from_config(self):
584613
}
585614

586615
def test_init_env(self):
587-
self.check_config("init_env", self.INIT_ENV_CONFIG)
616+
self.check_config("init_env", self.INIT_ENV_CONFIG, self.INIT_ENV_PRECONFIG)
588617

589618
def test_init_env_dev_mode(self):
590-
config = dict(self.INIT_ENV_CONFIG,
619+
preconfig = dict(self.INIT_ENV_PRECONFIG,
591620
allocator='debug',
592621
dev_mode=1)
593-
self.check_config("init_env_dev_mode", config)
594-
595-
def test_init_env_dev_mode(self):
596622
config = dict(self.INIT_ENV_CONFIG,
597-
allocator='malloc',
598623
dev_mode=1)
599-
self.check_config("init_env_dev_mode_alloc", config)
624+
self.check_config("init_env_dev_mode", config, preconfig)
625+
626+
def test_init_env_dev_mode(self):
627+
preconfig = dict(self.INIT_ENV_PRECONFIG,
628+
allocator='malloc',
629+
dev_mode=1)
630+
config = dict(self.INIT_ENV_CONFIG)
631+
self.check_config("init_env_dev_mode_alloc", config, preconfig)
600632

601633
def test_init_dev_mode(self):
602-
config = {
634+
preconfig = {
635+
'allocator': 'debug',
603636
'dev_mode': 1,
637+
}
638+
config = {
604639
'faulthandler': 1,
605-
'allocator': 'debug',
606640
}
607-
self.check_config("init_dev_mode", config)
641+
self.check_config("init_dev_mode", config, preconfig)
608642

609643
def test_init_isolated(self):
610-
config = {
644+
preconfig = {
611645
'isolated': 1,
612646
'use_environment': 0,
647+
}
648+
config = {
613649
'user_site_directory': 0,
614650
}
615-
self.check_config("init_isolated", config)
651+
self.check_config("init_isolated", config, preconfig)
616652

617653

618654
if __name__ == "__main__":

Modules/_testcapimodule.c

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4675,27 +4675,9 @@ decode_locale_ex(PyObject *self, PyObject *args)
46754675

46764676

46774677
static PyObject *
4678-
get_global_config(PyObject *self, PyObject *Py_UNUSED(args))
4678+
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
46794679
{
4680-
return _Py_GetGlobalVariablesAsDict();
4681-
}
4682-
4683-
4684-
static PyObject *
4685-
get_core_config(PyObject *self, PyObject *Py_UNUSED(args))
4686-
{
4687-
PyInterpreterState *interp = _PyInterpreterState_Get();
4688-
const _PyCoreConfig *config = _PyInterpreterState_GetCoreConfig(interp);
4689-
return _PyCoreConfig_AsDict(config);
4690-
}
4691-
4692-
4693-
static PyObject *
4694-
get_main_config(PyObject *self, PyObject *Py_UNUSED(args))
4695-
{
4696-
PyInterpreterState *interp = _PyInterpreterState_Get();
4697-
const _PyMainInterpreterConfig *config = _PyInterpreterState_GetMainConfig(interp);
4698-
return _PyMainInterpreterConfig_AsDict(config);
4680+
return _Py_GetConfigsAsDict();
46994681
}
47004682

47014683

@@ -4942,9 +4924,7 @@ static PyMethodDef TestMethods[] = {
49424924
{"bad_get", (PyCFunction)(void(*)(void))bad_get, METH_FASTCALL},
49434925
{"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
49444926
{"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
4945-
{"get_global_config", get_global_config, METH_NOARGS},
4946-
{"get_core_config", get_core_config, METH_NOARGS},
4947-
{"get_main_config", get_main_config, METH_NOARGS},
4927+
{"get_configs", get_configs, METH_NOARGS},
49484928
#ifdef Py_REF_DEBUG
49494929
{"negative_refcount", negative_refcount, METH_NOARGS},
49504930
#endif

0 commit comments

Comments
 (0)