[Python-checkins] bpo-42260: Add _PyConfig_FromDict() (GH-23167)

vstinner webhook-mailer at python.org
Thu Nov 5 12:12:42 EST 2020


https://github.com/python/cpython/commit/f3cb81431574453aac3b6dcadb3120331e6a8f1c
commit: f3cb81431574453aac3b6dcadb3120331e6a8f1c
branch: master
author: Victor Stinner <vstinner at python.org>
committer: vstinner <vstinner at python.org>
date: 2020-11-05T18:12:33+01:00
summary:

bpo-42260: Add _PyConfig_FromDict() (GH-23167)

* Rename config_as_dict() to _PyConfig_AsDict().
* Add 'module_search_paths_set' to _PyConfig_AsDict().
* Add _PyConfig_FromDict().
* Add get_config() and set_config() to _testinternalcapi.
* Add config_check_consistency().

files:
A Lib/test/_test_embed_set_config.py
M Include/internal/pycore_initconfig.h
M Lib/test/test_embed.py
M Modules/_testinternalcapi.c
M Python/initconfig.c
M Python/sysmodule.c

diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h
index df7ad779f477c..325be5494d406 100644
--- a/Include/internal/pycore_initconfig.h
+++ b/Include/internal/pycore_initconfig.h
@@ -158,6 +158,9 @@ extern PyStatus _PyConfig_SetPyArgv(
     PyConfig *config,
     const _PyArgv *args);
 
+PyAPI_FUNC(PyObject*) _PyConfig_AsDict(const PyConfig *config);
+PyAPI_FUNC(int) _PyConfig_FromDict(PyConfig *config, PyObject *dict);
+
 
 /* --- Function used for testing ---------------------------------- */
 
diff --git a/Lib/test/_test_embed_set_config.py b/Lib/test/_test_embed_set_config.py
new file mode 100644
index 0000000000000..7c913811ded5c
--- /dev/null
+++ b/Lib/test/_test_embed_set_config.py
@@ -0,0 +1,243 @@
+# bpo-42260: Test _PyInterpreterState_GetConfigCopy()
+# and _PyInterpreterState_SetConfig().
+#
+# Test run in a subinterpreter since set_config(get_config())
+# does reset sys attributes to their state of the Python startup
+# (before the site module is run).
+
+import _testinternalcapi
+import os
+import sys
+import unittest
+
+
+MS_WINDOWS = (os.name == 'nt')
+MAX_HASH_SEED = 4294967295
+
+class SetConfigTests(unittest.TestCase):
+    def setUp(self):
+        self.old_config = _testinternalcapi.get_config()
+        self.sys_copy = dict(sys.__dict__)
+
+    def tearDown(self):
+        self.set_config(parse_argv=0)
+        sys.__dict__.clear()
+        sys.__dict__.update(self.sys_copy)
+
+    def set_config(self, **kwargs):
+        _testinternalcapi.set_config(self.old_config | kwargs)
+
+    def check(self, **kwargs):
+        self.set_config(**kwargs)
+        for key, value in kwargs.items():
+            self.assertEqual(getattr(sys, key), value,
+                             (key, value))
+
+    def test_set_invalid(self):
+        invalid_uint = -1
+        NULL = None
+        invalid_wstr = NULL
+        # PyWideStringList strings must be non-NULL
+        invalid_wstrlist = ["abc", NULL, "def"]
+
+        type_tests = []
+        value_tests = [
+            # enum
+            ('_config_init', 0),
+            ('_config_init', 4),
+            # unsigned long
+            ("hash_seed", -1),
+            ("hash_seed", MAX_HASH_SEED + 1),
+        ]
+
+        # int (unsigned)
+        options = [
+            '_config_init',
+            'isolated',
+            'use_environment',
+            'dev_mode',
+            'install_signal_handlers',
+            'use_hash_seed',
+            'faulthandler',
+            'tracemalloc',
+            'import_time',
+            'show_ref_count',
+            'dump_refs',
+            'malloc_stats',
+            'parse_argv',
+            'site_import',
+            'bytes_warning',
+            'inspect',
+            'interactive',
+            'optimization_level',
+            'parser_debug',
+            'write_bytecode',
+            'verbose',
+            'quiet',
+            'user_site_directory',
+            'configure_c_stdio',
+            'buffered_stdio',
+            'pathconfig_warnings',
+            'module_search_paths_set',
+            'skip_source_first_line',
+            '_install_importlib',
+            '_init_main',
+            '_isolated_interpreter',
+        ]
+        if MS_WINDOWS:
+            options.append('legacy_windows_stdio')
+        for key in options:
+            value_tests.append((key, invalid_uint))
+            type_tests.append((key, "abc"))
+            type_tests.append((key, 2.0))
+
+        # wchar_t*
+        for key in (
+            'filesystem_encoding',
+            'filesystem_errors',
+            'stdio_encoding',
+            'stdio_errors',
+            'check_hash_pycs_mode',
+            'program_name',
+            'platlibdir',
+            'executable',
+            'base_executable',
+            'prefix',
+            'base_prefix',
+            'exec_prefix',
+            'base_exec_prefix',
+            # optional wstr:
+            # 'pythonpath_env'
+            # 'home',
+            # 'pycache_prefix'
+            # 'run_command'
+            # 'run_module'
+            # 'run_filename'
+        ):
+            value_tests.append((key, invalid_wstr))
+            type_tests.append((key, b'bytes'))
+            type_tests.append((key, 123))
+
+        # PyWideStringList
+        for key in (
+            'orig_argv',
+            'argv',
+            'xoptions',
+            'warnoptions',
+            'module_search_paths',
+        ):
+            value_tests.append((key, invalid_wstrlist))
+            type_tests.append((key, 123))
+            type_tests.append((key, "abc"))
+            type_tests.append((key, [123]))
+            type_tests.append((key, [b"bytes"]))
+
+
+        if MS_WINDOWS:
+            value_tests.append(('legacy_windows_stdio', invalid_uint))
+
+        for exc_type, tests in (
+            (ValueError, value_tests),
+            (TypeError, type_tests),
+        ):
+            for key, value in tests:
+                config = self.old_config | {key: value}
+                with self.subTest(key=key, value=value, exc_type=exc_type):
+                    with self.assertRaises(exc_type):
+                        _testinternalcapi.set_config(config)
+
+    def test_flags(self):
+        for sys_attr, key, value in (
+            ("debug", "parser_debug", 1),
+            ("inspect", "inspect", 2),
+            ("interactive", "interactive", 3),
+            ("optimize", "optimization_level", 4),
+            ("verbose", "verbose", 1),
+            ("bytes_warning", "bytes_warning", 10),
+            ("quiet", "quiet", 11),
+            ("isolated", "isolated", 12),
+        ):
+            with self.subTest(sys=sys_attr, key=key, value=value):
+                self.set_config(**{key: value, 'parse_argv': 0})
+                self.assertEqual(getattr(sys.flags, sys_attr), value)
+
+        self.set_config(write_bytecode=0)
+        self.assertEqual(sys.flags.dont_write_bytecode, True)
+        self.assertEqual(sys.dont_write_bytecode, True)
+
+        self.set_config(write_bytecode=1)
+        self.assertEqual(sys.flags.dont_write_bytecode, False)
+        self.assertEqual(sys.dont_write_bytecode, False)
+
+        self.set_config(user_site_directory=0, isolated=0)
+        self.assertEqual(sys.flags.no_user_site, 1)
+        self.set_config(user_site_directory=1, isolated=0)
+        self.assertEqual(sys.flags.no_user_site, 0)
+
+        self.set_config(site_import=0)
+        self.assertEqual(sys.flags.no_site, 1)
+        self.set_config(site_import=1)
+        self.assertEqual(sys.flags.no_site, 0)
+
+        self.set_config(dev_mode=0)
+        self.assertEqual(sys.flags.dev_mode, False)
+        self.set_config(dev_mode=1)
+        self.assertEqual(sys.flags.dev_mode, True)
+
+        self.set_config(use_environment=0, isolated=0)
+        self.assertEqual(sys.flags.ignore_environment, 1)
+        self.set_config(use_environment=1, isolated=0)
+        self.assertEqual(sys.flags.ignore_environment, 0)
+
+        self.set_config(use_hash_seed=1, hash_seed=0)
+        self.assertEqual(sys.flags.hash_randomization, 0)
+        self.set_config(use_hash_seed=0, hash_seed=0)
+        self.assertEqual(sys.flags.hash_randomization, 1)
+        self.set_config(use_hash_seed=1, hash_seed=123)
+        self.assertEqual(sys.flags.hash_randomization, 1)
+
+    def test_options(self):
+        self.check(warnoptions=[])
+        self.check(warnoptions=["default", "ignore"])
+
+        self.set_config(xoptions=[])
+        self.assertEqual(sys._xoptions, {})
+        self.set_config(xoptions=["dev", "tracemalloc=5"])
+        self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"})
+
+    def test_pathconfig(self):
+        self.check(
+            executable='executable',
+            prefix="prefix",
+            base_prefix="base_prefix",
+            exec_prefix="exec_prefix",
+            base_exec_prefix="base_exec_prefix",
+            platlibdir="platlibdir")
+
+        self.set_config(base_executable="base_executable")
+        self.assertEqual(sys._base_executable, "base_executable")
+
+    def test_path(self):
+        self.set_config(module_search_paths_set=1,
+                        module_search_paths=['a', 'b', 'c'])
+        self.assertEqual(sys.path, ['a', 'b', 'c'])
+
+        # Leave sys.path unchanged if module_search_paths_set=0
+        self.set_config(module_search_paths_set=0,
+                        module_search_paths=['new_path'])
+        self.assertEqual(sys.path, ['a', 'b', 'c'])
+
+    def test_argv(self):
+        self.set_config(parse_argv=0,
+                        argv=['python_program', 'args'],
+                        orig_argv=['orig', 'orig_args'])
+        self.assertEqual(sys.argv, ['python_program', 'args'])
+        self.assertEqual(sys.orig_argv, ['orig', 'orig_args'])
+
+    def test_pycache_prefix(self):
+        self.check(pycache_prefix=None)
+        self.check(pycache_prefix="pycache_prefix")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 36a0e77e14cee..918206151938d 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -30,6 +30,8 @@
 # _PyCoreConfig_InitIsolatedConfig()
 API_ISOLATED = 3
 
+MAX_HASH_SEED = 4294967295
+
 
 def debug_build(program):
     program = os.path.basename(program)
@@ -382,6 +384,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         'exec_prefix': GET_DEFAULT_CONFIG,
         'base_exec_prefix': GET_DEFAULT_CONFIG,
         'module_search_paths': GET_DEFAULT_CONFIG,
+        'module_search_paths_set': 1,
         'platlibdir': sys.platlibdir,
 
         'site_import': 1,
@@ -1408,6 +1411,17 @@ def test_get_argc_argv(self):
         # ignore output
 
 
+class SetConfigTests(unittest.TestCase):
+    def test_set_config(self):
+        # bpo-42260: Test _PyInterpreterState_SetConfig()
+        cmd = [sys.executable, '-I', '-m', 'test._test_embed_set_config']
+        proc = subprocess.run(cmd,
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.PIPE)
+        self.assertEqual(proc.returncode, 0,
+                         (proc.returncode, proc.stdout, proc.stderr))
+
+
 class AuditingTests(EmbeddingTestsMixin, unittest.TestCase):
     def test_open_code_hook(self):
         self.run_embedded_interpreter("test_open_code_hook")
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index ad74af8363ef4..be144bfba0266 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -13,9 +13,10 @@
 
 #include "Python.h"
 #include "pycore_bitutils.h"     // _Py_bswap32()
-#include "pycore_initconfig.h"   // _Py_GetConfigsAsDict()
-#include "pycore_hashtable.h"    // _Py_hashtable_new()
 #include "pycore_gc.h"           // PyGC_Head
+#include "pycore_hashtable.h"    // _Py_hashtable_new()
+#include "pycore_initconfig.h"   // _Py_GetConfigsAsDict()
+#include "pycore_interp.h"       // _PyInterpreterState_GetConfigCopy()
 
 
 static PyObject *
@@ -231,6 +232,38 @@ test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
 }
 
 
+static PyObject *
+test_get_config(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args))
+{
+    PyConfig config;
+    PyConfig_InitIsolatedConfig(&config);
+    if (_PyInterpreterState_GetConfigCopy(&config) < 0) {
+        PyConfig_Clear(&config);
+        return NULL;
+    }
+    PyObject *dict = _PyConfig_AsDict(&config);
+    PyConfig_Clear(&config);
+    return dict;
+}
+
+
+static PyObject *
+test_set_config(PyObject *Py_UNUSED(self), PyObject *dict)
+{
+    PyConfig config;
+    PyConfig_InitIsolatedConfig(&config);
+    if (_PyConfig_FromDict(&config, dict) < 0) {
+        PyConfig_Clear(&config);
+        return NULL;
+    }
+    if (_PyInterpreterState_SetConfig(&config) < 0) {
+        return NULL;
+    }
+    PyConfig_Clear(&config);
+    Py_RETURN_NONE;
+}
+
+
 static PyMethodDef TestMethods[] = {
     {"get_configs", get_configs, METH_NOARGS},
     {"get_recursion_depth", get_recursion_depth, METH_NOARGS},
@@ -238,6 +271,8 @@ static PyMethodDef TestMethods[] = {
     {"test_popcount", test_popcount, METH_NOARGS},
     {"test_bit_length", test_bit_length, METH_NOARGS},
     {"test_hashtable", test_hashtable, METH_NOARGS},
+    {"get_config", test_get_config, METH_NOARGS},
+    {"set_config", test_set_config, METH_O},
     {NULL, NULL} /* sentinel */
 };
 
diff --git a/Python/initconfig.c b/Python/initconfig.c
index de496ac7b522b..d54d5b7a9991d 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -577,6 +577,74 @@ Py_GetArgcArgv(int *argc, wchar_t ***argv)
      ? _PyStatus_ERR("cannot decode " NAME) \
      : _PyStatus_NO_MEMORY())
 
+#define MAX_HASH_SEED 4294967295UL
+
+
+#ifndef NDEBUG
+static int
+config_check_consistency(const PyConfig *config)
+{
+    /* Check config consistency */
+    assert(config->isolated >= 0);
+    assert(config->use_environment >= 0);
+    assert(config->dev_mode >= 0);
+    assert(config->install_signal_handlers >= 0);
+    assert(config->use_hash_seed >= 0);
+    assert(config->hash_seed <= MAX_HASH_SEED);
+    assert(config->faulthandler >= 0);
+    assert(config->tracemalloc >= 0);
+    assert(config->import_time >= 0);
+    assert(config->show_ref_count >= 0);
+    assert(config->dump_refs >= 0);
+    assert(config->malloc_stats >= 0);
+    assert(config->site_import >= 0);
+    assert(config->bytes_warning >= 0);
+    assert(config->inspect >= 0);
+    assert(config->interactive >= 0);
+    assert(config->optimization_level >= 0);
+    assert(config->parser_debug >= 0);
+    assert(config->write_bytecode >= 0);
+    assert(config->verbose >= 0);
+    assert(config->quiet >= 0);
+    assert(config->user_site_directory >= 0);
+    assert(config->parse_argv >= 0);
+    assert(config->configure_c_stdio >= 0);
+    assert(config->buffered_stdio >= 0);
+    assert(config->program_name != NULL);
+    assert(_PyWideStringList_CheckConsistency(&config->orig_argv));
+    assert(_PyWideStringList_CheckConsistency(&config->argv));
+    /* sys.argv must be non-empty: empty argv is replaced with [''] */
+    assert(config->argv.length >= 1);
+    assert(_PyWideStringList_CheckConsistency(&config->xoptions));
+    assert(_PyWideStringList_CheckConsistency(&config->warnoptions));
+    assert(_PyWideStringList_CheckConsistency(&config->module_search_paths));
+    assert(config->module_search_paths_set >= 0);
+    if (config->_install_importlib) {
+        /* don't check config->module_search_paths */
+        assert(config->executable != NULL);
+        assert(config->base_executable != NULL);
+        assert(config->prefix != NULL);
+        assert(config->base_prefix != NULL);
+        assert(config->exec_prefix != NULL);
+        assert(config->base_exec_prefix != NULL);
+    }
+    assert(config->platlibdir != NULL);
+    assert(config->filesystem_encoding != NULL);
+    assert(config->filesystem_errors != NULL);
+    assert(config->stdio_encoding != NULL);
+    assert(config->stdio_errors != NULL);
+#ifdef MS_WINDOWS
+    assert(config->legacy_windows_stdio >= 0);
+#endif
+    /* -c and -m options are exclusive */
+    assert(!(config->run_command != NULL && config->run_module != NULL));
+    assert(config->check_hash_pycs_mode != NULL);
+    assert(config->_install_importlib >= 0);
+    assert(config->pathconfig_warnings >= 0);
+    return 1;
+}
+#endif
+
 
 /* Free memory allocated in config, but don't clear all attributes */
 void
@@ -880,8 +948,8 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
 }
 
 
-static PyObject *
-config_as_dict(const PyConfig *config)
+PyObject *
+_PyConfig_AsDict(const PyConfig *config)
 {
     PyObject *dict = PyDict_New();
     if (dict == NULL) {
@@ -936,6 +1004,7 @@ config_as_dict(const PyConfig *config)
     SET_ITEM_WSTRLIST(warnoptions);
     SET_ITEM_WSTR(pythonpath_env);
     SET_ITEM_WSTR(home);
+    SET_ITEM_INT(module_search_paths_set);
     SET_ITEM_WSTRLIST(module_search_paths);
     SET_ITEM_WSTR(executable);
     SET_ITEM_WSTR(base_executable);
@@ -987,6 +1056,285 @@ config_as_dict(const PyConfig *config)
 }
 
 
+static PyObject*
+config_dict_get(PyObject *dict, const char *name)
+{
+    PyObject *item = PyDict_GetItemString(dict, name);
+    if (item == NULL) {
+        PyErr_Format(PyExc_ValueError, "missing config key: %s", name);
+        return NULL;
+    }
+    return item;
+}
+
+
+static void
+config_dict_invalid_value(const char *name)
+{
+    PyErr_Format(PyExc_ValueError, "invalid config value: %s", name);
+}
+
+
+static void
+config_dict_invalid_type(const char *name)
+{
+    PyErr_Format(PyExc_TypeError, "invalid config type: %s", name);
+}
+
+
+static int
+config_dict_get_int(PyObject *dict, const char *name, int *result)
+{
+    PyObject *item = config_dict_get(dict, name);
+    if (item == NULL) {
+        return -1;
+    }
+    int value = _PyLong_AsInt(item);
+    if (value == -1 && PyErr_Occurred()) {
+        if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+            config_dict_invalid_type(name);
+        }
+        else {
+            config_dict_invalid_value(name);
+        }
+        return -1;
+    }
+    *result = value;
+    return 0;
+}
+
+
+static int
+config_dict_get_ulong(PyObject *dict, const char *name, unsigned long *result)
+{
+    PyObject *item = config_dict_get(dict, name);
+    if (item == NULL) {
+        return -1;
+    }
+    unsigned long value = PyLong_AsUnsignedLong(item);
+    if (value == (unsigned long)-1 && PyErr_Occurred()) {
+        config_dict_invalid_value(name);
+        return -1;
+    }
+    *result = value;
+    return 0;
+}
+
+
+static int
+config_dict_get_wstr(PyObject *dict, const char *name, PyConfig *config,
+                     wchar_t **result)
+{
+    PyObject *item = config_dict_get(dict, name);
+    if (item == NULL) {
+        return -1;
+    }
+    PyStatus status;
+    if (item == Py_None) {
+        status = PyConfig_SetString(config, result, NULL);
+    }
+    else if (!PyUnicode_Check(item)) {
+        config_dict_invalid_type(name);
+        return -1;
+    }
+    else {
+        wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL);
+        if (wstr == NULL) {
+            return -1;
+        }
+        status = PyConfig_SetString(config, result, wstr);
+        PyMem_Free(wstr);
+    }
+    if (_PyStatus_EXCEPTION(status)) {
+        PyErr_NoMemory();
+        return -1;
+    }
+    return 0;
+}
+
+
+static int
+config_dict_get_wstrlist(PyObject *dict, const char *name, PyConfig *config,
+                         PyWideStringList *result)
+{
+    PyObject *list = config_dict_get(dict, name);
+    if (list == NULL) {
+        return -1;
+    }
+
+    if (!PyList_CheckExact(list)) {
+        config_dict_invalid_type(name);
+        return -1;
+    }
+
+    PyWideStringList wstrlist = _PyWideStringList_INIT;
+    for (Py_ssize_t i=0; i < PyList_GET_SIZE(list); i++) {
+        PyObject *item = PyList_GET_ITEM(list, i);
+
+        if (item == Py_None) {
+            config_dict_invalid_value(name);
+            goto error;
+        }
+        else if (!PyUnicode_Check(item)) {
+            config_dict_invalid_type(name);
+            goto error;
+        }
+        wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL);
+        if (wstr == NULL) {
+            goto error;
+        }
+        PyStatus status = PyWideStringList_Append(&wstrlist, wstr);
+        PyMem_Free(wstr);
+        if (_PyStatus_EXCEPTION(status)) {
+            PyErr_NoMemory();
+            goto error;
+        }
+    }
+
+    if (_PyWideStringList_Copy(result, &wstrlist) < 0) {
+        PyErr_NoMemory();
+        goto error;
+    }
+    _PyWideStringList_Clear(&wstrlist);
+    return 0;
+
+error:
+    _PyWideStringList_Clear(&wstrlist);
+    return -1;
+}
+
+
+int
+_PyConfig_FromDict(PyConfig *config, PyObject *dict)
+{
+    if (!PyDict_Check(dict)) {
+        PyErr_SetString(PyExc_TypeError, "dict expected");
+        return -1;
+    }
+
+#define CHECK_VALUE(NAME, TEST) \
+    if (!(TEST)) { \
+        config_dict_invalid_value(NAME); \
+        return -1; \
+    }
+#define GET_UINT(KEY) \
+    do { \
+        if (config_dict_get_int(dict, #KEY, &config->KEY) < 0) { \
+            return -1; \
+        } \
+        CHECK_VALUE(#KEY, config->KEY >= 0); \
+    } while (0)
+#define GET_WSTR(KEY) \
+    do { \
+        if (config_dict_get_wstr(dict, #KEY, config, &config->KEY) < 0) { \
+            return -1; \
+        } \
+        CHECK_VALUE(#KEY, config->KEY != NULL); \
+    } while (0)
+#define GET_WSTR_OPT(KEY) \
+    do { \
+        if (config_dict_get_wstr(dict, #KEY, config, &config->KEY) < 0) { \
+            return -1; \
+        } \
+    } while (0)
+#define GET_WSTRLIST(KEY) \
+    do { \
+        if (config_dict_get_wstrlist(dict, #KEY, config, &config->KEY) < 0) { \
+            return -1; \
+        } \
+    } while (0)
+
+    GET_UINT(_config_init);
+    CHECK_VALUE("_config_init",
+                config->_config_init == _PyConfig_INIT_COMPAT
+                || config->_config_init == _PyConfig_INIT_PYTHON
+                || config->_config_init == _PyConfig_INIT_ISOLATED);
+    GET_UINT(isolated);
+    GET_UINT(use_environment);
+    GET_UINT(dev_mode);
+    GET_UINT(install_signal_handlers);
+    GET_UINT(use_hash_seed);
+    if (config_dict_get_ulong(dict, "hash_seed", &config->hash_seed) < 0) {
+        return -1;
+    }
+    CHECK_VALUE("hash_seed", config->hash_seed <= MAX_HASH_SEED);
+    GET_UINT(faulthandler);
+    GET_UINT(tracemalloc);
+    GET_UINT(import_time);
+    GET_UINT(show_ref_count);
+    GET_UINT(dump_refs);
+    GET_UINT(malloc_stats);
+    GET_WSTR(filesystem_encoding);
+    GET_WSTR(filesystem_errors);
+    GET_WSTR_OPT(pycache_prefix);
+    GET_UINT(parse_argv);
+    GET_WSTRLIST(orig_argv);
+    GET_WSTRLIST(argv);
+    GET_WSTRLIST(xoptions);
+    GET_WSTRLIST(warnoptions);
+    GET_UINT(site_import);
+    GET_UINT(bytes_warning);
+    GET_UINT(inspect);
+    GET_UINT(interactive);
+    GET_UINT(optimization_level);
+    GET_UINT(parser_debug);
+    GET_UINT(write_bytecode);
+    GET_UINT(verbose);
+    GET_UINT(quiet);
+    GET_UINT(user_site_directory);
+    GET_UINT(configure_c_stdio);
+    GET_UINT(buffered_stdio);
+    GET_WSTR(stdio_encoding);
+    GET_WSTR(stdio_errors);
+#ifdef MS_WINDOWS
+    GET_UINT(legacy_windows_stdio);
+#endif
+    GET_WSTR(check_hash_pycs_mode);
+
+    GET_UINT(pathconfig_warnings);
+    GET_WSTR(program_name);
+    GET_WSTR_OPT(pythonpath_env);
+    GET_WSTR_OPT(home);
+    GET_WSTR(platlibdir);
+
+    GET_UINT(module_search_paths_set);
+    GET_WSTRLIST(module_search_paths);
+    if (config->_install_importlib) {
+        GET_WSTR(executable);
+        GET_WSTR(base_executable);
+        GET_WSTR(prefix);
+        GET_WSTR(base_prefix);
+        GET_WSTR(exec_prefix);
+        GET_WSTR(base_exec_prefix);
+    }
+    else {
+        GET_WSTR_OPT(executable);
+        GET_WSTR_OPT(base_executable);
+        GET_WSTR_OPT(prefix);
+        GET_WSTR_OPT(base_prefix);
+        GET_WSTR_OPT(exec_prefix);
+        GET_WSTR_OPT(base_exec_prefix);
+    }
+
+    GET_UINT(skip_source_first_line);
+    GET_WSTR_OPT(run_command);
+    GET_WSTR_OPT(run_module);
+    GET_WSTR_OPT(run_filename);
+
+    GET_UINT(_install_importlib);
+    GET_UINT(_init_main);
+    GET_UINT(_isolated_interpreter);
+
+    assert(config_check_consistency(config));
+
+#undef CHECK_VALUE
+#undef GET_UINT
+#undef GET_WSTR
+#undef GET_WSTR_OPT
+    return 0;
+}
+
+
 static const char*
 config_get_env(const PyConfig *config, const char *name)
 {
@@ -1254,7 +1602,6 @@ config_init_home(PyConfig *config)
                               L"PYTHONHOME", "PYTHONHOME");
 }
 
-
 static PyStatus
 config_init_hash_seed(PyConfig *config)
 {
@@ -1268,7 +1615,7 @@ config_init_hash_seed(PyConfig *config)
         errno = 0;
         seed = strtoul(seed_text, (char **)&endptr, 10);
         if (*endptr != '\0'
-            || seed > 4294967295UL
+            || seed > MAX_HASH_SEED
             || (errno == ERANGE && seed == ULONG_MAX))
         {
             return _PyStatus_ERR("PYTHONHASHSEED must be \"random\" "
@@ -2532,58 +2879,7 @@ PyConfig_Read(PyConfig *config)
         goto done;
     }
 
-    /* Check config consistency */
-    assert(config->isolated >= 0);
-    assert(config->use_environment >= 0);
-    assert(config->dev_mode >= 0);
-    assert(config->install_signal_handlers >= 0);
-    assert(config->use_hash_seed >= 0);
-    assert(config->faulthandler >= 0);
-    assert(config->tracemalloc >= 0);
-    assert(config->site_import >= 0);
-    assert(config->bytes_warning >= 0);
-    assert(config->inspect >= 0);
-    assert(config->interactive >= 0);
-    assert(config->optimization_level >= 0);
-    assert(config->parser_debug >= 0);
-    assert(config->write_bytecode >= 0);
-    assert(config->verbose >= 0);
-    assert(config->quiet >= 0);
-    assert(config->user_site_directory >= 0);
-    assert(config->parse_argv >= 0);
-    assert(config->configure_c_stdio >= 0);
-    assert(config->buffered_stdio >= 0);
-    assert(config->program_name != NULL);
-    assert(_PyWideStringList_CheckConsistency(&config->argv));
-    /* sys.argv must be non-empty: empty argv is replaced with [''] */
-    assert(config->argv.length >= 1);
-    assert(_PyWideStringList_CheckConsistency(&config->xoptions));
-    assert(_PyWideStringList_CheckConsistency(&config->warnoptions));
-    assert(_PyWideStringList_CheckConsistency(&config->module_search_paths));
-    if (config->_install_importlib) {
-        assert(config->module_search_paths_set != 0);
-        /* don't check config->module_search_paths */
-        assert(config->executable != NULL);
-        assert(config->base_executable != NULL);
-        assert(config->prefix != NULL);
-        assert(config->base_prefix != NULL);
-        assert(config->exec_prefix != NULL);
-        assert(config->base_exec_prefix != NULL);
-    }
-    assert(config->platlibdir != NULL);
-    assert(config->filesystem_encoding != NULL);
-    assert(config->filesystem_errors != NULL);
-    assert(config->stdio_encoding != NULL);
-    assert(config->stdio_errors != NULL);
-#ifdef MS_WINDOWS
-    assert(config->legacy_windows_stdio >= 0);
-#endif
-    /* -c and -m options are exclusive */
-    assert(!(config->run_command != NULL && config->run_module != NULL));
-    assert(config->check_hash_pycs_mode != NULL);
-    assert(config->_install_importlib >= 0);
-    assert(config->pathconfig_warnings >= 0);
-    assert(_PyWideStringList_CheckConsistency(&config->orig_argv));
+    assert(config_check_consistency(config));
 
     status = _PyStatus_OK();
 
@@ -2628,7 +2924,7 @@ _Py_GetConfigsAsDict(void)
 
     /* core config */
     const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
-    dict = config_as_dict(config);
+    dict = _PyConfig_AsDict(config);
     if (dict == NULL) {
         goto error;
     }
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 60b2494651235..ae4f0eeb2ee66 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -2922,7 +2922,9 @@ _PySys_UpdateConfig(PyThreadState *tstate)
 #define SET_SYS_FROM_WSTR(KEY, VALUE) \
         SET_SYS(KEY, PyUnicode_FromWideChar(VALUE, -1));
 
-    COPY_LIST("path", config->module_search_paths);
+    if (config->module_search_paths_set) {
+        COPY_LIST("path", config->module_search_paths);
+    }
 
     SET_SYS_FROM_WSTR("executable", config->executable);
     SET_SYS_FROM_WSTR("_base_executable", config->base_executable);



More information about the Python-checkins mailing list