[Python-checkins] [3.7] bpo-35233: Rewrite test_embed.InitConfigTests (GH-10524) (GH-10529)

Victor Stinner webhook-mailer at python.org
Tue Nov 13 20:01:57 EST 2018


https://github.com/python/cpython/commit/35c28d562ec7249f2a6aef8e326eadac130a1656
commit: 35c28d562ec7249f2a6aef8e326eadac130a1656
branch: 3.7
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-11-14T02:01:52+01:00
summary:

[3.7] bpo-35233: Rewrite test_embed.InitConfigTests (GH-10524) (GH-10529)

* Add C functions:

  * _Py_GetGlobalVariablesAsDict()
  * _PyCoreConfig_AsDict()
  * _PyMainInterpreterConfig_AsDict()

* Add new _testcapi methods:

  * get_global_config()
  * get_core_config()
  * get_main_config()

* test.pythoninfo: get global, core and main configuration
* _testembed now serializes global, core and main configurations
  using JSON to reuse _Py_GetGlobalVariablesAsDict(),
  _PyCoreConfig_AsDict() and _PyMainInterpreterConfig_AsDict(),
  rather than duplicating code.
* test_embed.InitConfigTests now test much more configuration
  variables

files:
M Include/pylifecycle.h
M Lib/test/pythoninfo.py
M Lib/test/test_embed.py
M Modules/_testcapimodule.c
M Modules/main.c
M Programs/_testembed.c

diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h
index 119296194934..41f7d14af9e4 100644
--- a/Include/pylifecycle.h
+++ b/Include/pylifecycle.h
@@ -61,11 +61,14 @@ PyAPI_FUNC(_PyInitError) _Py_InitializeFromConfig(
 PyAPI_FUNC(void) _Py_Initialize_ReadEnvVarsNoAlloc(void);
 #endif
 
+PyAPI_FUNC(PyObject *) _Py_GetGlobalVariablesAsDict(void);
+
 PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *);
 PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *);
 PyAPI_FUNC(int) _PyCoreConfig_Copy(
     _PyCoreConfig *config,
     const _PyCoreConfig *config2);
+PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
 PyAPI_FUNC(void) _PyCoreConfig_SetGlobalConfig(
     const _PyCoreConfig *config);
 
@@ -77,6 +80,8 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
 PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
     _PyMainInterpreterConfig *config,
     const _PyMainInterpreterConfig *config2);
+PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
+    const _PyMainInterpreterConfig *config);
 
 PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
         PyInterpreterState *interp,
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 4a563e9baae2..9257fdf332a6 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -142,7 +142,7 @@ def collect_platform(info_add):
     info_add('platform.python_implementation',
              platform.python_implementation())
     info_add('platform.platform',
-             platform.platform(aliased=True, terse=True))
+             platform.platform(aliased=True))
 
 
 def collect_locale(info_add):
@@ -525,6 +525,33 @@ def collect_cc(info_add):
     info_add('CC.version', text)
 
 
+def collect_gdbm(info_add):
+    try:
+        from _gdbm import _GDBM_VERSION
+    except ImportError:
+        return
+
+    info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
+
+
+def collect_get_config(info_add):
+    # Dump global configuration variables, _PyCoreConfig
+    # and _PyMainInterpreterConfig
+    try:
+        from _testcapi import get_global_config, get_core_config, get_main_config
+    except ImportError:
+        return
+
+    for prefix, get_config_func in (
+        ('global_config', get_global_config),
+        ('core_config', get_core_config),
+        ('main_config', get_main_config),
+    ):
+        config = get_config_func()
+        for key in sorted(config):
+            info_add('%s[%s]' % (prefix, key), repr(config[key]))
+
+
 def collect_info(info):
     error = False
     info_add = info.add
@@ -552,6 +579,8 @@ def collect_info(info):
         collect_testcapi,
         collect_resource,
         collect_cc,
+        collect_gdbm,
+        collect_get_config,
 
         # Collecting from tests should be last as they have side effects.
         collect_test_socket,
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 29274580339f..62a20abf0377 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -3,6 +3,7 @@
 import unittest
 
 from collections import namedtuple
+import json
 import os
 import re
 import subprocess
@@ -251,12 +252,52 @@ def test_initialize_pymain(self):
 
 class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
     maxDiff = 4096
-    DEFAULT_CONFIG = {
+    UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
+                        else 'surrogateescape')
+    # FIXME: untested core configuration variables
+    UNTESTED_CORE_CONFIG = (
+        'base_exec_prefix',
+        'base_prefix',
+        'exec_prefix',
+        'executable',
+        'home',
+        'module_search_path_env',
+        'module_search_paths',
+        'prefix',
+    )
+    # FIXME: untested main configuration variables
+    UNTESTED_MAIN_CONFIG = (
+        'install_signal_handlers',
+        'module_search_path',
+    )
+    DEFAULT_GLOBAL_CONFIG = {
+        'Py_BytesWarningFlag': 0,
+        'Py_DebugFlag': 0,
+        'Py_DontWriteBytecodeFlag': 0,
+        'Py_FrozenFlag': 0,
+        'Py_HasFileSystemDefaultEncoding': 0,
+        'Py_HashRandomizationFlag': 1,
+        'Py_InspectFlag': 0,
+        'Py_InteractiveFlag': 0,
+        'Py_IsolatedFlag': 0,
+        'Py_NoSiteFlag': 0,
+        'Py_NoUserSiteDirectory': 0,
+        'Py_OptimizeFlag': 0,
+        'Py_QuietFlag': 0,
+        'Py_UnbufferedStdioFlag': 0,
+        'Py_VerboseFlag': 0,
+    }
+    if os.name == 'nt':
+        DEFAULT_GLOBAL_CONFIG['Py_HasFileSystemDefaultEncoding'] = 1
+        DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsFSEncodingFlag'] = 0
+        DEFAULT_GLOBAL_CONFIG['Py_LegacyWindowsStdioFlag'] = 0
+
+    DEFAULT_CORE_CONFIG = {
         'install_signal_handlers': 1,
-        'Py_IgnoreEnvironmentFlag': 0,
+        'ignore_environment': 0,
         'use_hash_seed': 0,
         'hash_seed': 0,
-        'allocator': '(null)',
+        'allocator': None,
         'dev_mode': 0,
         'faulthandler': 0,
         'tracemalloc': 0,
@@ -265,34 +306,42 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         'show_alloc_count': 0,
         'dump_refs': 0,
         'malloc_stats': 0,
-        'utf8_mode': 0,
 
+        'utf8_mode': 0,
         'coerce_c_locale': 0,
         'coerce_c_locale_warn': 0,
 
         'program_name': './_testembed',
-        'argc': 0,
-        'argv': '[]',
-        'program': '(null)',
+        'argv': [],
+        'program': None,
 
-        'Py_IsolatedFlag': 0,
-        'Py_NoSiteFlag': 0,
-        'Py_BytesWarningFlag': 0,
-        'Py_InspectFlag': 0,
-        'Py_InteractiveFlag': 0,
-        'Py_OptimizeFlag': 0,
-        'Py_DebugFlag': 0,
-        'Py_DontWriteBytecodeFlag': 0,
-        'Py_VerboseFlag': 0,
-        'Py_QuietFlag': 0,
-        'Py_NoUserSiteDirectory': 0,
-        'Py_UnbufferedStdioFlag': 0,
+        'xoptions': [],
+        'warnoptions': [],
 
         '_disable_importlib': 0,
-        'Py_FrozenFlag': 0,
     }
 
-    def check_config(self, testname, expected):
+    def get_filesystem_encoding(self, isolated, env):
+        code = ('import codecs, locale, sys; '
+                'print(sys.getfilesystemencoding(), '
+                'sys.getfilesystemencodeerrors())')
+        args = (sys.executable, '-c', code)
+        env = dict(env)
+        if not isolated:
+            env['PYTHONCOERCECLOCALE'] = '0'
+            env['PYTHONUTF8'] = '0'
+        proc = subprocess.run(args, text=True, env=env,
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.PIPE)
+        if proc.returncode:
+            raise Exception(f"failed to get the locale encoding: "
+                            f"stdout={proc.stdout!r} stderr={proc.stderr!r}")
+        out = proc.stdout.rstrip()
+        return out.split()
+
+    def check_config(self, testname, expected, expected_global):
+        expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
+
         env = dict(os.environ)
         for key in list(env):
             if key.startswith('PYTHON'):
@@ -301,42 +350,78 @@ def check_config(self, testname, expected):
         # on the current locale
         env['PYTHONCOERCECLOCALE'] = '0'
         env['PYTHONUTF8'] = '0'
+
         out, err = self.run_embedded_interpreter(testname, env=env)
         # Ignore err
 
-        expected = dict(self.DEFAULT_CONFIG, **expected)
-        for key, value in expected.items():
-            expected[key] = str(value)
+        config = json.loads(out)
+        core_config = config['core_config']
+        executable = core_config['executable']
+        main_config = config['main_config']
+
+        for key in self.UNTESTED_MAIN_CONFIG:
+            del main_config[key]
+
+        expected_main = {
+            'argv': [],
+            'prefix': sys.prefix,
+            'executable': core_config['executable'],
+            'base_prefix': sys.base_prefix,
+            'base_exec_prefix': sys.base_exec_prefix,
+            'warnoptions': core_config['warnoptions'],
+            'xoptions': {},
+            'exec_prefix': core_config['exec_prefix'],
+        }
+        self.assertEqual(main_config, expected_main)
 
-        config = {}
-        for line in out.splitlines():
-            key, value = line.split(' = ', 1)
-            config[key] = value
-        self.assertEqual(config, expected)
+        expected_global = dict(self.DEFAULT_GLOBAL_CONFIG, **expected_global)
+
+        if 'Py_FileSystemDefaultEncoding' not in expected_global:
+            isolated = expected_global['Py_IsolatedFlag']
+            fs_encoding, fs_errors = self.get_filesystem_encoding(isolated, env)
+            expected_global['Py_FileSystemDefaultEncodeErrors'] = fs_errors
+            expected_global['Py_FileSystemDefaultEncoding'] = fs_encoding
+
+        for global_key, core_key in (
+            ('Py_UTF8Mode', 'utf8_mode'),
+            ('Py_IgnoreEnvironmentFlag', 'ignore_environment'),
+        ):
+            expected_global[global_key] = core_config[core_key]
+
+        self.assertEqual(config['global_config'], expected_global)
+
+        for key in self.UNTESTED_CORE_CONFIG:
+            core_config.pop(key, None)
+        self.assertEqual(core_config, expected)
 
     def test_init_default_config(self):
-        self.check_config("init_default_config", {})
+        self.check_config("init_default_config", {}, {})
 
     def test_init_global_config(self):
-        config = {
+        core_config = {
             'program_name': './globalvar',
-            'Py_NoSiteFlag': 1,
+            'utf8_mode': 1,
+        }
+        global_config = {
             'Py_BytesWarningFlag': 1,
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_HasFileSystemDefaultEncoding': 1,
+            'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
+            'Py_FileSystemDefaultEncoding': 'utf-8',
             'Py_InspectFlag': 1,
             'Py_InteractiveFlag': 1,
+            'Py_NoSiteFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
             'Py_OptimizeFlag': 2,
-            'Py_DontWriteBytecodeFlag': 1,
-            'Py_VerboseFlag': 1,
             'Py_QuietFlag': 1,
-            'Py_UnbufferedStdioFlag': 1,
-            'utf8_mode': 1,
-            'Py_NoUserSiteDirectory': 1,
+            'Py_VerboseFlag': 1,
             'Py_FrozenFlag': 1,
+            'Py_UnbufferedStdioFlag': 1,
         }
-        self.check_config("init_global_config", config)
+        self.check_config("init_global_config", core_config, global_config)
 
     def test_init_from_config(self):
-        config = {
+        core_config = {
             'install_signal_handlers': 0,
             'use_hash_seed': 1,
             'hash_seed': 123,
@@ -354,10 +439,14 @@ def test_init_from_config(self):
 
             'faulthandler': 1,
         }
-        self.check_config("init_from_config", config)
+        global_config = {
+            'Py_HasFileSystemDefaultEncoding': 1,
+            'Py_NoUserSiteDirectory': 0,
+        }
+        self.check_config("init_from_config", core_config, global_config)
 
     def test_init_env(self):
-        config = {
+        core_config = {
             'use_hash_seed': 1,
             'hash_seed': 42,
             'allocator': 'malloc_debug',
@@ -365,32 +454,39 @@ def test_init_env(self):
             'import_time': 1,
             'malloc_stats': 1,
             'utf8_mode': 1,
+            'faulthandler': 1,
+            'dev_mode': 1,
+        }
+        global_config = {
+            'Py_DontWriteBytecodeFlag': 1,
+            'Py_HasFileSystemDefaultEncoding': 1,
             'Py_InspectFlag': 1,
+            'Py_NoUserSiteDirectory': 1,
             'Py_OptimizeFlag': 2,
-            'Py_DontWriteBytecodeFlag': 1,
-            'Py_VerboseFlag': 1,
             'Py_UnbufferedStdioFlag': 1,
-            'Py_NoUserSiteDirectory': 1,
-            'faulthandler': 1,
-            'dev_mode': 1,
+            'Py_VerboseFlag': 1,
+            'Py_FileSystemDefaultEncoding': 'utf-8',
+            'Py_FileSystemDefaultEncodeErrors': self.UTF8_MODE_ERRORS,
         }
-        self.check_config("init_env", config)
+        self.check_config("init_env", core_config, global_config)
 
     def test_init_dev_mode(self):
-        config = {
+        core_config = {
             'dev_mode': 1,
             'faulthandler': 1,
             'allocator': 'debug',
         }
-        self.check_config("init_dev_mode", config)
+        self.check_config("init_dev_mode", core_config, {})
 
     def test_init_isolated(self):
-        config = {
+        core_config = {
+            'ignore_environment': 1,
+        }
+        global_config = {
             'Py_IsolatedFlag': 1,
-            'Py_IgnoreEnvironmentFlag': 1,
             'Py_NoUserSiteDirectory': 1,
         }
-        self.check_config("init_isolated", config)
+        self.check_config("init_isolated", core_config, global_config)
 
 
 if __name__ == "__main__":
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 174b767f2b90..eafe3477e447 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4551,6 +4551,31 @@ new_hamt(PyObject *self, PyObject *args)
 }
 
 
+static PyObject *
+get_global_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    return _Py_GetGlobalVariablesAsDict();
+}
+
+
+static PyObject *
+get_core_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    PyInterpreterState *interp = PyThreadState_GET()->interp;
+    const _PyCoreConfig *config = &interp->core_config;
+    return _PyCoreConfig_AsDict(config);
+}
+
+
+static PyObject *
+get_main_config(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    PyInterpreterState *interp = PyThreadState_GET()->interp;
+    const _PyMainInterpreterConfig *config = &interp->config;
+    return _PyMainInterpreterConfig_AsDict(config);
+}
+
+
 static PyMethodDef TestMethods[] = {
     {"raise_exception",         raise_exception,                 METH_VARARGS},
     {"raise_memoryerror",   (PyCFunction)raise_memoryerror,  METH_NOARGS},
@@ -4777,6 +4802,9 @@ static PyMethodDef TestMethods[] = {
     {"get_mapping_items", get_mapping_items, METH_O},
     {"test_pythread_tss_key_state", test_pythread_tss_key_state, METH_VARARGS},
     {"hamt", new_hamt, METH_NOARGS},
+    {"get_global_config", get_global_config, METH_NOARGS},
+    {"get_core_config", get_core_config, METH_NOARGS},
+    {"get_main_config", get_main_config, METH_NOARGS},
     {NULL, NULL} /* sentinel */
 };
 
diff --git a/Modules/main.c b/Modules/main.c
index 7771d2750097..ab7ac86bad91 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -1297,7 +1297,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config,
 
 
 static PyObject*
-wstrlist_as_pylist(int len, wchar_t **list)
+_Py_wstrlist_as_pylist(int len, wchar_t **list)
 {
     assert(list != NULL || len < 1);
 
@@ -1361,6 +1361,77 @@ pymain_update_sys_path(_PyMain *pymain, PyObject *path0)
 }
 
 
+PyObject *
+_Py_GetGlobalVariablesAsDict(void)
+{
+    PyObject *dict, *obj;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM(KEY, EXPR) \
+        do { \
+            obj = (EXPR); \
+            if (obj == NULL) { \
+                return NULL; \
+            } \
+            int res = PyDict_SetItemString(dict, (KEY), obj); \
+            Py_DECREF(obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+#define SET_ITEM_INT(VAR) \
+    SET_ITEM(#VAR, PyLong_FromLong(VAR))
+#define FROM_STRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromString(STR) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_STR(VAR) \
+    SET_ITEM(#VAR, FROM_STRING(VAR))
+
+    SET_ITEM_STR(Py_FileSystemDefaultEncoding);
+    SET_ITEM_INT(Py_HasFileSystemDefaultEncoding);
+    SET_ITEM_STR(Py_FileSystemDefaultEncodeErrors);
+
+    SET_ITEM_INT(Py_UTF8Mode);
+    SET_ITEM_INT(Py_DebugFlag);
+    SET_ITEM_INT(Py_VerboseFlag);
+    SET_ITEM_INT(Py_QuietFlag);
+    SET_ITEM_INT(Py_InteractiveFlag);
+    SET_ITEM_INT(Py_InspectFlag);
+
+    SET_ITEM_INT(Py_OptimizeFlag);
+    SET_ITEM_INT(Py_NoSiteFlag);
+    SET_ITEM_INT(Py_BytesWarningFlag);
+    SET_ITEM_INT(Py_FrozenFlag);
+    SET_ITEM_INT(Py_IgnoreEnvironmentFlag);
+    SET_ITEM_INT(Py_DontWriteBytecodeFlag);
+    SET_ITEM_INT(Py_NoUserSiteDirectory);
+    SET_ITEM_INT(Py_UnbufferedStdioFlag);
+    SET_ITEM_INT(Py_HashRandomizationFlag);
+    SET_ITEM_INT(Py_IsolatedFlag);
+
+#ifdef MS_WINDOWS
+    SET_ITEM_INT(Py_LegacyWindowsFSEncodingFlag);
+    SET_ITEM_INT(Py_LegacyWindowsStdioFlag);
+#endif
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef FROM_STRING
+#undef SET_ITEM
+#undef SET_ITEM_INT
+#undef SET_ITEM_STR
+}
+
+
 void
 _PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config)
 {
@@ -2432,6 +2503,110 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
 }
 
 
+PyObject *
+_PyCoreConfig_AsDict(const _PyCoreConfig *config)
+{
+    PyObject *dict, *obj;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define FROM_STRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromString(STR) \
+        : (Py_INCREF(Py_None), Py_None))
+#define FROM_WSTRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromWideChar(STR, -1) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM(KEY, EXPR) \
+        do { \
+            obj = (EXPR); \
+            if (obj == NULL) { \
+                return NULL; \
+            } \
+            int res = PyDict_SetItemString(dict, (KEY), obj); \
+            Py_DECREF(obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+
+    SET_ITEM("install_signal_handlers",
+             PyLong_FromLong(config->install_signal_handlers));
+    SET_ITEM("ignore_environment",
+             PyLong_FromLong(config->ignore_environment));
+    SET_ITEM("use_hash_seed",
+             PyLong_FromLong(config->use_hash_seed));
+    SET_ITEM("hash_seed",
+             PyLong_FromUnsignedLong(config->hash_seed));
+    SET_ITEM("allocator",
+             FROM_STRING(config->allocator));
+    SET_ITEM("dev_mode",
+             PyLong_FromLong(config->dev_mode));
+    SET_ITEM("faulthandler",
+             PyLong_FromLong(config->faulthandler));
+    SET_ITEM("tracemalloc",
+             PyLong_FromLong(config->tracemalloc));
+    SET_ITEM("import_time",
+             PyLong_FromLong(config->import_time));
+    SET_ITEM("show_ref_count",
+             PyLong_FromLong(config->show_ref_count));
+    SET_ITEM("show_alloc_count",
+             PyLong_FromLong(config->show_alloc_count));
+    SET_ITEM("dump_refs",
+             PyLong_FromLong(config->dump_refs));
+    SET_ITEM("malloc_stats",
+             PyLong_FromLong(config->malloc_stats));
+    SET_ITEM("coerce_c_locale",
+             PyLong_FromLong(config->coerce_c_locale));
+    SET_ITEM("coerce_c_locale_warn",
+             PyLong_FromLong(config->coerce_c_locale_warn));
+    SET_ITEM("utf8_mode",
+             PyLong_FromLong(config->utf8_mode));
+    SET_ITEM("program_name",
+             FROM_WSTRING(config->program_name));
+    SET_ITEM("argv",
+             _Py_wstrlist_as_pylist(config->argc, config->argv));
+    SET_ITEM("program",
+             FROM_WSTRING(config->program));
+    SET_ITEM("xoptions",
+             _Py_wstrlist_as_pylist(config->nxoption, config->xoptions));
+    SET_ITEM("warnoptions",
+             _Py_wstrlist_as_pylist(config->nwarnoption, config->warnoptions));
+    SET_ITEM("module_search_path_env",
+             FROM_WSTRING(config->module_search_path_env));
+    SET_ITEM("home",
+             FROM_WSTRING(config->home));
+    SET_ITEM("module_search_paths",
+             _Py_wstrlist_as_pylist(config->nmodule_search_path, config->module_search_paths));
+    SET_ITEM("executable",
+             FROM_WSTRING(config->executable));
+    SET_ITEM("prefix",
+             FROM_WSTRING(config->prefix));
+    SET_ITEM("base_prefix",
+             FROM_WSTRING(config->base_prefix));
+    SET_ITEM("exec_prefix",
+             FROM_WSTRING(config->exec_prefix));
+    SET_ITEM("base_exec_prefix",
+             FROM_WSTRING(config->base_exec_prefix));
+    SET_ITEM("_disable_importlib",
+             PyLong_FromLong(config->_disable_importlib));
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef FROM_STRING
+#undef FROM_WSTRING
+#undef SET_ITEM
+}
+
+
 void
 _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config)
 {
@@ -2501,6 +2676,57 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
 }
 
 
+PyObject*
+_PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
+{
+    PyObject *dict, *obj;
+    int res;
+
+    dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM(KEY, ATTR) \
+        do { \
+            obj = config->ATTR; \
+            if (obj == NULL) { \
+                obj = Py_None; \
+            } \
+            res = PyDict_SetItemString(dict, (KEY), obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+
+    obj = PyLong_FromLong(config->install_signal_handlers);
+    if (obj == NULL) {
+        goto fail;
+    }
+    res = PyDict_SetItemString(dict, "install_signal_handlers", obj);
+    Py_DECREF(obj);
+    if (res < 0) {
+        goto fail;
+    }
+
+    SET_ITEM("argv", argv);
+    SET_ITEM("executable", executable);
+    SET_ITEM("prefix", prefix);
+    SET_ITEM("base_prefix", base_prefix);
+    SET_ITEM("exec_prefix", exec_prefix);
+    SET_ITEM("base_exec_prefix", base_exec_prefix);
+    SET_ITEM("warnoptions", warnoptions);
+    SET_ITEM("xoptions", xoptions);
+    SET_ITEM("module_search_path", module_search_path);
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef SET_ITEM
+}
 
 
 _PyInitError
@@ -2530,7 +2756,7 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
 #define COPY_WSTRLIST(ATTR, LEN, LIST) \
     do { \
         if (ATTR == NULL) { \
-            ATTR = wstrlist_as_pylist(LEN, LIST); \
+            ATTR = _Py_wstrlist_as_pylist(LEN, LIST); \
             if (ATTR == NULL) { \
                 return _Py_INIT_NO_MEMORY(); \
             } \
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 6c35f9586bfe..b198962db3a4 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -293,92 +293,77 @@ static int test_initialize_pymain(void)
 }
 
 
-static void
-dump_config(void)
+static int
+dump_config_impl(void)
 {
-#define ASSERT_EQUAL(a, b) \
-    if ((a) != (b)) { \
-        printf("ERROR: %s != %s (%i != %i)\n", #a, #b, (a), (b)); \
-        exit(1); \
+    PyObject *config = NULL;
+    PyObject *dict = NULL;
+
+    config = PyDict_New();
+    if (config == NULL) {
+        goto error;
     }
-#define ASSERT_STR_EQUAL(a, b) \
-    if ((a) == NULL || (b == NULL) || wcscmp((a), (b)) != 0) { \
-        printf("ERROR: %s != %s ('%ls' != '%ls')\n", #a, #b, (a), (b)); \
-        exit(1); \
+
+    /* global config */
+    dict = _Py_GetGlobalVariablesAsDict();
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(config, "global_config", dict) < 0) {
+        goto error;
     }
+    Py_CLEAR(dict);
 
+    /* core config */
     PyInterpreterState *interp = PyThreadState_Get()->interp;
-    _PyCoreConfig *config = &interp->core_config;
-
-    printf("install_signal_handlers = %i\n", config->install_signal_handlers);
-
-    printf("Py_IgnoreEnvironmentFlag = %i\n", Py_IgnoreEnvironmentFlag);
+    const _PyCoreConfig *core_config = &interp->core_config;
+    dict = _PyCoreConfig_AsDict(core_config);
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(config, "core_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
 
-    printf("use_hash_seed = %i\n", config->use_hash_seed);
-    printf("hash_seed = %lu\n", config->hash_seed);
+    /* main config */
+    const _PyMainInterpreterConfig *main_config = &interp->config;
+    dict = _PyMainInterpreterConfig_AsDict(main_config);
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(config, "main_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
+
+    PyObject *json = PyImport_ImportModule("json");
+    PyObject *res = PyObject_CallMethod(json, "dumps", "O", config);
+    Py_DECREF(json);
+    Py_CLEAR(config);
+    if (res == NULL) {
+        goto error;
+    }
 
-    printf("allocator = %s\n", config->allocator);
+    PySys_FormatStdout("%S\n", res);
+    Py_DECREF(res);
 
-    printf("dev_mode = %i\n", config->dev_mode);
-    printf("faulthandler = %i\n", config->faulthandler);
-    printf("tracemalloc = %i\n", config->tracemalloc);
-    printf("import_time = %i\n", config->import_time);
-    printf("show_ref_count = %i\n", config->show_ref_count);
-    printf("show_alloc_count = %i\n", config->show_alloc_count);
-    printf("dump_refs = %i\n", config->dump_refs);
-    printf("malloc_stats = %i\n", config->malloc_stats);
+    return 0;
 
-    printf("coerce_c_locale = %i\n", config->coerce_c_locale);
-    printf("coerce_c_locale_warn = %i\n", config->coerce_c_locale_warn);
-    printf("utf8_mode = %i\n", config->utf8_mode);
+error:
+    Py_XDECREF(config);
+    Py_XDECREF(dict);
+    return -1;
+}
 
-    printf("program_name = %ls\n", config->program_name);
-    ASSERT_STR_EQUAL(config->program_name, Py_GetProgramName());
 
-    printf("argc = %i\n", config->argc);
-    printf("argv = [");
-    for (int i=0; i < config->argc; i++) {
-        if (i) {
-            printf(", ");
-        }
-        printf("\"%ls\"", config->argv[i]);
+static void
+dump_config(void)
+{
+    if (dump_config_impl() < 0) {
+        fprintf(stderr, "failed to dump the configuration:\n");
+        PyErr_Print();
     }
-    printf("]\n");
-
-    printf("program = %ls\n", config->program);
-    /* FIXME: test xoptions */
-    /* FIXME: test warnoptions */
-    /* FIXME: test module_search_path_env */
-    /* FIXME: test home */
-    /* FIXME: test module_search_paths */
-    /* FIXME: test executable */
-    /* FIXME: test prefix */
-    /* FIXME: test base_prefix */
-    /* FIXME: test exec_prefix */
-    /* FIXME: test base_exec_prefix */
-    /* FIXME: test dll_path */
-
-    printf("Py_IsolatedFlag = %i\n", Py_IsolatedFlag);
-    printf("Py_NoSiteFlag = %i\n", Py_NoSiteFlag);
-    printf("Py_BytesWarningFlag = %i\n", Py_BytesWarningFlag);
-    printf("Py_InspectFlag = %i\n", Py_InspectFlag);
-    printf("Py_InteractiveFlag = %i\n", Py_InteractiveFlag);
-    printf("Py_OptimizeFlag = %i\n", Py_OptimizeFlag);
-    printf("Py_DebugFlag = %i\n", Py_DebugFlag);
-    printf("Py_DontWriteBytecodeFlag = %i\n", Py_DontWriteBytecodeFlag);
-    printf("Py_VerboseFlag = %i\n", Py_VerboseFlag);
-    printf("Py_QuietFlag = %i\n", Py_QuietFlag);
-    printf("Py_NoUserSiteDirectory = %i\n", Py_NoUserSiteDirectory);
-    printf("Py_UnbufferedStdioFlag = %i\n", Py_UnbufferedStdioFlag);
-    /* FIXME: test legacy_windows_fs_encoding */
-    /* FIXME: test legacy_windows_stdio */
-
-    printf("_disable_importlib = %i\n", config->_disable_importlib);
-    /* cannot test _Py_CheckHashBasedPycsMode: the symbol is not exported */
-    printf("Py_FrozenFlag = %i\n", Py_FrozenFlag);
-
-#undef ASSERT_EQUAL
-#undef ASSERT_STR_EQUAL
 }
 
 



More information about the Python-checkins mailing list