[Python-checkins] bpo-35233: Fix _PyMainInterpreterConfig_Copy() (GH-10519)

Victor Stinner webhook-mailer at python.org
Tue Nov 13 13:59:35 EST 2018


https://github.com/python/cpython/commit/00b137c72f90fbc39a6cd7e48b37c58d19977180
commit: 00b137c72f90fbc39a6cd7e48b37c58d19977180
branch: master
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2018-11-13T19:59:26+01:00
summary:

bpo-35233: Fix _PyMainInterpreterConfig_Copy() (GH-10519)

* Fix _PyMainInterpreterConfig_Copy():
   copy 'install_signal_handlers' attribute
* Add _PyMainInterpreterConfig_AsDict()
* Add unit tests on the main interpreter configuration
  to test_embed.InitConfigTests
* test.pythoninfo: log also main_config

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 ca1f24f2a174..3d9365d3156a 100644
--- a/Include/pylifecycle.h
+++ b/Include/pylifecycle.h
@@ -37,6 +37,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 21763d58c039..2b5d6e20a259 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -534,15 +534,25 @@ def collect_gdbm(info_add):
     info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
 
 
-def collect_get_coreconfig(info_add):
+def collect_get_config(info_add):
+    # Dump _PyCoreConfig and _PyMainInterpreterConfig
     try:
         from _testcapi import get_coreconfig
     except ImportError:
-        return
+        pass
+    else:
+        config = get_coreconfig()
+        for key in sorted(config):
+            info_add('core_config[%s]' % key, repr(config[key]))
 
-    config = get_coreconfig()
-    for key in sorted(config):
-        info_add('coreconfig[%s]' % key, repr(config[key]))
+    try:
+        from _testcapi import get_mainconfig
+    except ImportError:
+        pass
+    else:
+        config = get_mainconfig()
+        for key in sorted(config):
+            info_add('main_config[%s]' % key, repr(config[key]))
 
 
 def collect_info(info):
@@ -573,7 +583,7 @@ def collect_info(info):
         collect_resource,
         collect_cc,
         collect_gdbm,
-        collect_get_coreconfig,
+        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 80233a54b0b0..cd10376c2496 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -255,9 +255,11 @@ def test_initialize_pymain(self):
 
 class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
     maxDiff = 4096
+    CORE_CONFIG_REGEX = re.compile(r"^core_config\[([^]]*)\] = (.*)$")
+    MAIN_CONFIG_REGEX = re.compile(r"^main_config\[([^]]*)\] = (.*)$")
     UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
                         else 'surrogateescape')
-    DEFAULT_CONFIG = {
+    DEFAULT_CORE_CONFIG = {
         'install_signal_handlers': 1,
         'use_environment': 1,
         'use_hash_seed': 0,
@@ -338,7 +340,7 @@ def get_filesystem_encoding(self, isolated, env):
         return out.split()
 
     def check_config(self, testname, expected):
-        expected = dict(self.DEFAULT_CONFIG, **expected)
+        expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
 
         env = dict(os.environ)
         for key in list(env):
@@ -367,11 +369,39 @@ def check_config(self, testname, expected):
         out, err = self.run_embedded_interpreter(testname, env=env)
         # Ignore err
 
-        config = {}
+        core_config = {}
+        main_config = {}
         for line in out.splitlines():
-            key, value = line.split(' = ', 1)
-            config[key] = value
-        self.assertEqual(config, expected)
+            match = self.CORE_CONFIG_REGEX.match(line)
+            if match is not None:
+                key = match.group(1)
+                value = match.group(2)
+                core_config[key] = value
+            else:
+                match = self.MAIN_CONFIG_REGEX.match(line)
+                if match is None:
+                    raise ValueError(f"failed to parse line {line!r}")
+                key = match.group(1)
+                value = match.group(2)
+                main_config[key] = value
+        self.assertEqual(core_config, expected)
+
+        pycache_prefix = core_config['pycache_prefix']
+        if pycache_prefix != NULL_STR:
+            pycache_prefix = repr(pycache_prefix)
+        else:
+            pycache_prefix = "NULL"
+        expected_main = {
+            'install_signal_handlers': core_config['install_signal_handlers'],
+            'argv': '[]',
+            'prefix': repr(sys.prefix),
+            'base_prefix': repr(sys.base_prefix),
+            'base_exec_prefix': repr(sys.base_exec_prefix),
+            'warnoptions': '[]',
+            'xoptions': '{}',
+            'pycache_prefix': pycache_prefix,
+        }
+        self.assertEqual(main_config, expected_main)
 
     def test_init_default_config(self):
         self.check_config("init_default_config", {})
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index bc1f63071892..205b668bdfcb 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4702,6 +4702,15 @@ get_coreconfig(PyObject *self, PyObject *Py_UNUSED(args))
 }
 
 
+static PyObject *
+get_mainconfig(PyObject *self, PyObject *Py_UNUSED(args))
+{
+    PyInterpreterState *interp = _PyInterpreterState_Get();
+    const _PyMainInterpreterConfig *config = &interp->config;
+    return _PyMainInterpreterConfig_AsDict(config);
+}
+
+
 #ifdef Py_REF_DEBUG
 static PyObject *
 negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
@@ -4948,6 +4957,7 @@ static PyMethodDef TestMethods[] = {
     {"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
     {"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
     {"get_coreconfig", get_coreconfig, METH_NOARGS},
+    {"get_mainconfig", get_mainconfig, METH_NOARGS},
 #ifdef Py_REF_DEBUG
     {"negative_refcount", negative_refcount, METH_NOARGS},
 #endif
diff --git a/Modules/main.c b/Modules/main.c
index 281707ae5b24..3e51ef8e0bca 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -1441,31 +1441,88 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
 {
     _PyMainInterpreterConfig_Clear(config);
 
-#define COPY_ATTR(ATTR) \
+#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
+#define COPY_OBJ_ATTR(OBJ_ATTR) \
     do { \
-        if (config2->ATTR != NULL) { \
-            config->ATTR = config_copy_attr(config2->ATTR); \
-            if (config->ATTR == NULL) { \
+        if (config2->OBJ_ATTR != NULL) { \
+            config->OBJ_ATTR = config_copy_attr(config2->OBJ_ATTR); \
+            if (config->OBJ_ATTR == NULL) { \
                 return -1; \
             } \
         } \
     } while (0)
 
-    COPY_ATTR(argv);
-    COPY_ATTR(executable);
-    COPY_ATTR(prefix);
-    COPY_ATTR(base_prefix);
-    COPY_ATTR(exec_prefix);
-    COPY_ATTR(base_exec_prefix);
-    COPY_ATTR(warnoptions);
-    COPY_ATTR(xoptions);
-    COPY_ATTR(module_search_path);
-    COPY_ATTR(pycache_prefix);
+    COPY_ATTR(install_signal_handlers);
+    COPY_OBJ_ATTR(argv);
+    COPY_OBJ_ATTR(executable);
+    COPY_OBJ_ATTR(prefix);
+    COPY_OBJ_ATTR(base_prefix);
+    COPY_OBJ_ATTR(exec_prefix);
+    COPY_OBJ_ATTR(base_exec_prefix);
+    COPY_OBJ_ATTR(warnoptions);
+    COPY_OBJ_ATTR(xoptions);
+    COPY_OBJ_ATTR(module_search_path);
+    COPY_OBJ_ATTR(pycache_prefix);
 #undef COPY_ATTR
+#undef COPY_OBJ_ATTR
     return 0;
 }
 
 
+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);
+    SET_ITEM("pycache_prefix", pycache_prefix);
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+
+#undef SET_ITEM
+}
+
+
 _PyInitError
 _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
                               const _PyCoreConfig *config)
diff --git a/Programs/_testembed.c b/Programs/_testembed.c
index 99772eacbdc4..27060718b492 100644
--- a/Programs/_testembed.c
+++ b/Programs/_testembed.c
@@ -293,7 +293,7 @@ static int test_initialize_pymain(void)
 
 
 static void
-dump_config(void)
+dump_core_config(void)
 {
 #define ASSERT_EQUAL(a, b) \
     if ((a) != (b)) { \
@@ -309,37 +309,37 @@ dump_config(void)
     PyInterpreterState *interp = _PyInterpreterState_Get();
     _PyCoreConfig *config = &interp->core_config;
 
-    printf("install_signal_handlers = %i\n", config->install_signal_handlers);
+    printf("core_config[install_signal_handlers] = %i\n", config->install_signal_handlers);
 
-    printf("use_environment = %i\n", config->use_environment);
+    printf("core_config[use_environment] = %i\n", config->use_environment);
     ASSERT_EQUAL(config->use_environment, !Py_IgnoreEnvironmentFlag);
 
-    printf("use_hash_seed = %i\n", config->use_hash_seed);
-    printf("hash_seed = %lu\n", config->hash_seed);
+    printf("core_config[use_hash_seed] = %i\n", config->use_hash_seed);
+    printf("core_config[hash_seed] = %lu\n", config->hash_seed);
 
-    printf("allocator = %s\n", config->allocator);
+    printf("core_config[allocator] = %s\n", config->allocator);
 
-    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);
+    printf("core_config[dev_mode] = %i\n", config->dev_mode);
+    printf("core_config[faulthandler] = %i\n", config->faulthandler);
+    printf("core_config[tracemalloc] = %i\n", config->tracemalloc);
+    printf("core_config[import_time] = %i\n", config->import_time);
+    printf("core_config[show_ref_count] = %i\n", config->show_ref_count);
+    printf("core_config[show_alloc_count] = %i\n", config->show_alloc_count);
+    printf("core_config[dump_refs] = %i\n", config->dump_refs);
+    printf("core_config[malloc_stats] = %i\n", config->malloc_stats);
 
-    printf("filesystem_encoding = %s\n", config->filesystem_encoding);
-    printf("filesystem_errors = %s\n", config->filesystem_errors);
-    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);
+    printf("core_config[filesystem_encoding] = %s\n", config->filesystem_encoding);
+    printf("core_config[filesystem_errors] = %s\n", config->filesystem_errors);
+    printf("core_config[coerce_c_locale] = %i\n", config->coerce_c_locale);
+    printf("core_config[coerce_c_locale_warn] = %i\n", config->coerce_c_locale_warn);
+    printf("core_config[utf8_mode] = %i\n", config->utf8_mode);
 
-    printf("pycache_prefix = %ls\n", config->pycache_prefix);
-    printf("program_name = %ls\n", config->program_name);
+    printf("core_config[pycache_prefix] = %ls\n", config->pycache_prefix);
+    printf("core_config[program_name] = %ls\n", config->program_name);
     ASSERT_STR_EQUAL(config->program_name, Py_GetProgramName());
 
-    printf("argc = %i\n", config->argc);
-    printf("argv = [");
+    printf("core_config[argc] = %i\n", config->argc);
+    printf("core_config[argv] = [");
     for (int i=0; i < config->argc; i++) {
         if (i) {
             printf(", ");
@@ -348,7 +348,7 @@ dump_config(void)
     }
     printf("]\n");
 
-    printf("program = %ls\n", config->program);
+    printf("core_config[program] = %ls\n", config->program);
     /* FIXME: test xoptions */
     /* FIXME: test warnoptions */
     /* FIXME: test module_search_path_env */
@@ -361,36 +361,75 @@ dump_config(void)
     /* FIXME: test base_exec_prefix */
     /* FIXME: test dll_path */
 
-    printf("isolated = %i\n", config->isolated);
+    printf("core_config[isolated] = %i\n", config->isolated);
     ASSERT_EQUAL(config->isolated, Py_IsolatedFlag);
-    printf("site_import = %i\n", config->site_import);
-    printf("bytes_warning = %i\n", config->bytes_warning);
-    printf("inspect = %i\n", config->inspect);
-    printf("interactive = %i\n", config->interactive);
-    printf("optimization_level = %i\n", config->optimization_level);
-    printf("parser_debug = %i\n", config->parser_debug);
-    printf("write_bytecode = %i\n", config->write_bytecode);
-    printf("verbose = %i\n", config->verbose);
+    printf("core_config[site_import] = %i\n", config->site_import);
+    printf("core_config[bytes_warning] = %i\n", config->bytes_warning);
+    printf("core_config[inspect] = %i\n", config->inspect);
+    printf("core_config[interactive] = %i\n", config->interactive);
+    printf("core_config[optimization_level] = %i\n", config->optimization_level);
+    printf("core_config[parser_debug] = %i\n", config->parser_debug);
+    printf("core_config[write_bytecode] = %i\n", config->write_bytecode);
+    printf("core_config[verbose] = %i\n", config->verbose);
     ASSERT_EQUAL(config->verbose, Py_VerboseFlag);
-    printf("quiet = %i\n", config->quiet);
-    printf("user_site_directory = %i\n", config->user_site_directory);
-    printf("buffered_stdio = %i\n", config->buffered_stdio);
+    printf("core_config[quiet] = %i\n", config->quiet);
+    printf("core_config[user_site_directory] = %i\n", config->user_site_directory);
+    printf("core_config[buffered_stdio] = %i\n", config->buffered_stdio);
     ASSERT_EQUAL(config->buffered_stdio, !Py_UnbufferedStdioFlag);
-    printf("stdio_encoding = %s\n", config->stdio_encoding);
-    printf("stdio_errors = %s\n", config->stdio_errors);
+    printf("core_config[stdio_encoding] = %s\n", config->stdio_encoding);
+    printf("core_config[stdio_errors] = %s\n", config->stdio_errors);
 
     /* FIXME: test legacy_windows_fs_encoding */
     /* FIXME: test legacy_windows_stdio */
 
-    printf("_install_importlib = %i\n", config->_install_importlib);
-    printf("_check_hash_pycs_mode = %s\n", config->_check_hash_pycs_mode);
-    printf("_frozen = %i\n", config->_frozen);
+    printf("core_config[_install_importlib] = %i\n", config->_install_importlib);
+    printf("core_config[_check_hash_pycs_mode] = %s\n", config->_check_hash_pycs_mode);
+    printf("core_config[_frozen] = %i\n", config->_frozen);
 
 #undef ASSERT_EQUAL
 #undef ASSERT_STR_EQUAL
 }
 
 
+static void
+dump_main_config(void)
+{
+    PyInterpreterState *interp = _PyInterpreterState_Get();
+    _PyMainInterpreterConfig *config = &interp->config;
+
+    printf("main_config[install_signal_handlers] = %i\n", config->install_signal_handlers);
+#define DUMP_ATTR(ATTR) \
+        do { \
+            if (config->ATTR != NULL) { \
+                PySys_FormatStdout("main_config[" #ATTR "] = %R\n", config->ATTR); \
+            } \
+            else { \
+                PySys_FormatStdout("main_config[" #ATTR "] = NULL\n"); \
+            } \
+        } while (0)
+
+    DUMP_ATTR(argv);
+    /* FIXME: DUMP_ATTR(executable); */
+    DUMP_ATTR(prefix);
+    DUMP_ATTR(base_prefix);
+    DUMP_ATTR(base_exec_prefix);
+    DUMP_ATTR(warnoptions);
+    DUMP_ATTR(xoptions);
+    /* FIXME: DUMP_ATTR(module_search_path); */
+    DUMP_ATTR(pycache_prefix);
+
+#undef DUMP_ATTR
+}
+
+
+static void
+dump_config(void)
+{
+    dump_core_config();
+    dump_main_config();
+}
+
+
 static int test_init_default_config(void)
 {
     _testembed_Py_Initialize();



More information about the Python-checkins mailing list