]> granicus.if.org Git - python/commitdiff
bpo-35233: InitConfigTests tests more config vars (GH-10541)
authorVictor Stinner <vstinner@redhat.com>
Wed, 14 Nov 2018 16:39:45 +0000 (17:39 +0100)
committerGitHub <noreply@github.com>
Wed, 14 Nov 2018 16:39:45 +0000 (17:39 +0100)
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.

Include/coreconfig.h
Include/pylifecycle.h
Lib/test/test_embed.py
Modules/main.c
Programs/_testembed.c
Python/coreconfig.c

index f71a364cc2c773544e50e3b99462993c9088afde..0ed3222a15e142e9a0fc0599b950376b02e9d3df 100644 (file)
@@ -359,9 +359,9 @@ PyAPI_FUNC(int) _PyCoreConfig_GetEnvDup(
     wchar_t *wname,
     char *name);
 
-/* Used by _testcapi.get_coreconfig() */
-PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
+/* Used by _testcapi.get_global_config() and _testcapi.get_core_config() */
 PyAPI_FUNC(PyObject *) _Py_GetGlobalVariablesAsDict(void);
+PyAPI_FUNC(PyObject *) _PyCoreConfig_AsDict(const _PyCoreConfig *config);
 #endif
 
 #ifdef __cplusplus
index 3d9365d3156a3cee5a6f48159f068321b7fce617..7d383aa0899a59ba99702f543a71816bf4435359 100644 (file)
@@ -37,6 +37,7 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
 PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
     _PyMainInterpreterConfig *config,
     const _PyMainInterpreterConfig *config2);
+/* Used by _testcapi.get_main_config() */
 PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
     const _PyMainInterpreterConfig *config);
 
index 03a11842fa6f8a5594db78ae724f56249d859b7e..75e31c2111222243799e5390d891b3b347ac1f69 100644 (file)
@@ -11,12 +11,15 @@ import subprocess
 import sys
 
 
+MS_WINDOWS = (os.name == 'nt')
+
+
 class EmbeddingTestsMixin:
     def setUp(self):
         here = os.path.abspath(__file__)
         basepath = os.path.dirname(os.path.dirname(os.path.dirname(here)))
         exename = "_testembed"
-        if sys.platform.startswith("win"):
+        if MS_WINDOWS:
             ext = ("_d" if "_d" in sys.executable else "") + ".exe"
             exename += ext
             exepath = os.path.dirname(sys.executable)
@@ -38,7 +41,7 @@ class EmbeddingTestsMixin:
         """Runs a test in the embedded interpreter"""
         cmd = [self.test_exe]
         cmd.extend(args)
-        if env is not None and sys.platform == 'win32':
+        if env is not None and MS_WINDOWS:
             # Windows requires at least the SYSTEMROOT environment variable to
             # start Python.
             env = env.copy()
@@ -199,7 +202,7 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
         """
         env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path))
         out, err = self.run_embedded_interpreter("pre_initialization_api", env=env)
-        if sys.platform == "win32":
+        if MS_WINDOWS:
             expected_path = self.test_exe
         else:
             expected_path = os.path.join(os.getcwd(), "spam")
@@ -253,25 +256,14 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
 
 class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
     maxDiff = 4096
-    UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
-                        else 'surrogateescape')
-    # FIXME: untested core configuration variables
+    UTF8_MODE_ERRORS = ('surrogatepass' if MS_WINDOWS else 'surrogateescape')
+
+    # core config
     UNTESTED_CORE_CONFIG = (
-        'base_exec_prefix',
-        'base_prefix',
+        # FIXME: untested core configuration variables
         'dll_path',
-        'exec_prefix',
         'executable',
-        'home',
-        'legacy_windows_fs_encoding',
-        'legacy_windows_stdio',
-        'module_search_path_env',
         'module_search_paths',
-        'prefix',
-    )
-    # FIXME: untested main configuration variables
-    UNTESTED_MAIN_CONFIG = (
-        'module_search_path',
     )
     DEFAULT_CORE_CONFIG = {
         'install_signal_handlers': 1,
@@ -304,6 +296,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         'xoptions': [],
         'warnoptions': [],
 
+        'module_search_path_env': None,
+        'home': None,
+        'prefix': sys.prefix,
+        'base_prefix': sys.base_prefix,
+        'exec_prefix': sys.exec_prefix,
+        'base_exec_prefix': sys.base_exec_prefix,
+
         'isolated': 0,
         'site_import': 1,
         'bytes_warning': 0,
@@ -325,6 +324,63 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         '_check_hash_pycs_mode': 'default',
         '_frozen': 0,
     }
+    if MS_WINDOWS:
+        DEFAULT_CORE_CONFIG.update({
+            'legacy_windows_fs_encoding': 0,
+            'legacy_windows_stdio': 0,
+        })
+
+    # main config
+    UNTESTED_MAIN_CONFIG = (
+        # FIXME: untested main configuration variables
+        'module_search_path',
+    )
+    COPY_MAIN_CONFIG = (
+        # Copy core config to main config for expected values
+        'argv',
+        'base_exec_prefix',
+        'base_prefix',
+        'exec_prefix',
+        'executable',
+        'install_signal_handlers',
+        'prefix',
+        'pycache_prefix',
+        'warnoptions',
+        # xoptions is created from core_config in check_main_config()
+    )
+
+    # global config
+    DEFAULT_GLOBAL_CONFIG = {
+        'Py_HasFileSystemDefaultEncoding': 0,
+        'Py_HashRandomizationFlag': 1,
+        '_Py_HasFileSystemDefaultEncodeErrors': 0,
+    }
+    COPY_GLOBAL_CONFIG = [
+        # Copy core config to global config for expected values
+        # True means that the core config value is inverted (0 => 1 and 1 => 0)
+        ('Py_BytesWarningFlag', 'bytes_warning'),
+        ('Py_DebugFlag', 'parser_debug'),
+        ('Py_DontWriteBytecodeFlag', 'write_bytecode', True),
+        ('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
+        ('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
+        ('Py_FrozenFlag', '_frozen'),
+        ('Py_IgnoreEnvironmentFlag', 'use_environment', True),
+        ('Py_InspectFlag', 'inspect'),
+        ('Py_InteractiveFlag', 'interactive'),
+        ('Py_IsolatedFlag', 'isolated'),
+        ('Py_NoSiteFlag', 'site_import', True),
+        ('Py_NoUserSiteDirectory', 'user_site_directory', True),
+        ('Py_OptimizeFlag', 'optimization_level'),
+        ('Py_QuietFlag', 'quiet'),
+        ('Py_UTF8Mode', 'utf8_mode'),
+        ('Py_UnbufferedStdioFlag', 'buffered_stdio', True),
+        ('Py_VerboseFlag', 'verbose'),
+    ]
+    if MS_WINDOWS:
+        COPY_GLOBAL_CONFIG.extend((
+            ('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'),
+            ('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
+        ))
 
     def get_stdio_encoding(self, env):
         code = 'import sys; print(sys.stdout.encoding, sys.stdout.errors)'
@@ -355,18 +411,31 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
         out = proc.stdout.rstrip()
         return out.split()
 
-    def check_config(self, testname, expected):
-        expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
+    def main_xoptions(self, xoptions_list):
+        xoptions = {}
+        for opt in xoptions_list:
+            if '=' in opt:
+                key, value = opt.split('=', 1)
+                xoptions[key] = value
+            else:
+                xoptions[opt] = True
+        return xoptions
 
-        env = dict(os.environ)
-        for key in list(env):
-            if key.startswith('PYTHON'):
-                del env[key]
-        # Disable C locale coercion and UTF-8 mode to not depend
-        # on the current locale
-        env['PYTHONCOERCECLOCALE'] = '0'
-        env['PYTHONUTF8'] = '0'
+    def check_main_config(self, config):
+        core_config = config['core_config']
+        main_config = config['main_config']
+
+        # main config
+        for key in self.UNTESTED_MAIN_CONFIG:
+            del main_config[key]
 
+        expected_main = {}
+        for key in self.COPY_MAIN_CONFIG:
+            expected_main[key] = core_config[key]
+        expected_main['xoptions'] = self.main_xoptions(core_config['xoptions'])
+        self.assertEqual(main_config, expected_main)
+
+    def check_core_config(self, config, expected, env):
         if expected['stdio_encoding'] is None or expected['stdio_errors'] is None:
             res = self.get_stdio_encoding(env)
             if expected['stdio_encoding'] is None:
@@ -380,59 +449,16 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             if expected['filesystem_errors'] is None:
                 expected['filesystem_errors'] = res[1]
 
-        out, err = self.run_embedded_interpreter(testname, env=env)
-        # Ignore err
+        core_config = dict(config['core_config'])
+        for key in self.UNTESTED_CORE_CONFIG:
+            core_config.pop(key, None)
+        self.assertEqual(core_config, expected)
 
-        config = json.loads(out)
+    def check_global_config(self, config):
         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 = {
-            'install_signal_handlers': core_config['install_signal_handlers'],
-            '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': {},
-            'pycache_prefix': core_config['pycache_prefix'],
-            'exec_prefix': core_config['exec_prefix'],
-        }
-        self.assertEqual(main_config, expected_main)
-
-
-        copy_global_config = [
-            ('Py_BytesWarningFlag', 'bytes_warning'),
-            ('Py_DebugFlag', 'parser_debug'),
-            ('Py_DontWriteBytecodeFlag', 'write_bytecode', True),
-            ('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
-            ('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
-            ('Py_FrozenFlag', '_frozen'),
-            ('Py_IgnoreEnvironmentFlag', 'use_environment', True),
-            ('Py_InspectFlag', 'inspect'),
-            ('Py_InteractiveFlag', 'interactive'),
-            ('Py_IsolatedFlag', 'isolated'),
-            ('Py_NoSiteFlag', 'site_import', True),
-            ('Py_NoUserSiteDirectory', 'user_site_directory', True),
-            ('Py_OptimizeFlag', 'optimization_level'),
-            ('Py_QuietFlag', 'quiet'),
-            ('Py_UTF8Mode', 'utf8_mode'),
-            ('Py_UnbufferedStdioFlag', 'buffered_stdio', True),
-            ('Py_VerboseFlag', 'verbose'),
-        ]
-        if os.name == 'nt':
-            copy_global_config.extend((
-                ('Py_LegacyWindowsFSEncodingFlag', 'legacy_windows_fs_encoding'),
-                ('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
-            ))
-
-        expected_global = {}
-        for item in copy_global_config:
+        expected_global = dict(self.DEFAULT_GLOBAL_CONFIG)
+        for item in self.COPY_GLOBAL_CONFIG:
             if len(item) == 3:
                 global_key, core_key, opposite = item
                 expected_global[global_key] = 0 if core_config[core_key] else 1
@@ -440,14 +466,28 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
                 global_key, core_key = item
                 expected_global[global_key] = core_config[core_key]
 
-        expected_global['Py_HasFileSystemDefaultEncoding'] = 0
-        expected_global['_Py_HasFileSystemDefaultEncodeErrors'] = 0
-        expected_global['Py_HashRandomizationFlag'] = 1
         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 check_config(self, testname, expected):
+        expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
+
+        env = dict(os.environ)
+        # Remove PYTHON* environment variables to get deterministic environment
+        for key in list(env):
+            if key.startswith('PYTHON'):
+                del env[key]
+        # Disable C locale coercion and UTF-8 mode to not depend
+        # on the current locale
+        env['PYTHONCOERCECLOCALE'] = '0'
+        env['PYTHONUTF8'] = '0'
+
+        out, err = self.run_embedded_interpreter(testname, env=env)
+        # Ignore err
+        config = json.loads(out)
+
+        self.check_core_config(config, expected, env)
+        self.check_main_config(config)
+        self.check_global_config(config)
 
     def test_init_default_config(self):
         self.check_config("init_default_config", {})
@@ -495,7 +535,10 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
 
             'pycache_prefix': 'conf_pycache_prefix',
             'program_name': './conf_program_name',
+            'argv': ['-c', 'pass'],
             'program': 'conf_program',
+            'xoptions': ['core_xoption1=3', 'core_xoption2=', 'core_xoption3'],
+            'warnoptions': ['default', 'error::ResourceWarning'],
 
             'site_import': 0,
             'bytes_warning': 1,
index 3e51ef8e0bca86096460ff742c4520f72887ece5..8e66ddded419a31cacd2c4a8f5e05cc2a011ed89 100644 (file)
@@ -1442,11 +1442,11 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
     _PyMainInterpreterConfig_Clear(config);
 
 #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
-#define COPY_OBJ_ATTR(OBJ_ATTR) \
+#define COPY_OBJ_ATTR(ATTR) \
     do { \
-        if (config2->OBJ_ATTR != NULL) { \
-            config->OBJ_ATTR = config_copy_attr(config2->OBJ_ATTR); \
-            if (config->OBJ_ATTR == NULL) { \
+        if (config2->ATTR != NULL) { \
+            config->ATTR = config_copy_attr(config2->ATTR); \
+            if (config->ATTR == NULL) { \
                 return -1; \
             } \
         } \
@@ -1480,38 +1480,42 @@ _PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
         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)
+#define SET_ITEM_INT(ATTR) \
+    do { \
+        obj = PyLong_FromLong(config->ATTR); \
+        if (obj == NULL) { \
+            goto fail; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        Py_DECREF(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;
-    }
+#define SET_ITEM_OBJ(ATTR) \
+    do { \
+        obj = config->ATTR; \
+        if (obj == NULL) { \
+            obj = Py_None; \
+        } \
+        res = PyDict_SetItemString(dict, #ATTR, obj); \
+        if (res < 0) { \
+            goto fail; \
+        } \
+    } while (0)
 
-    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);
+    SET_ITEM_INT(install_signal_handlers);
+    SET_ITEM_OBJ(argv);
+    SET_ITEM_OBJ(executable);
+    SET_ITEM_OBJ(prefix);
+    SET_ITEM_OBJ(base_prefix);
+    SET_ITEM_OBJ(exec_prefix);
+    SET_ITEM_OBJ(base_exec_prefix);
+    SET_ITEM_OBJ(warnoptions);
+    SET_ITEM_OBJ(xoptions);
+    SET_ITEM_OBJ(module_search_path);
+    SET_ITEM_OBJ(pycache_prefix);
 
     return dict;
 
@@ -1519,7 +1523,7 @@ fail:
     Py_DECREF(dict);
     return NULL;
 
-#undef SET_ITEM
+#undef SET_ITEM_OBJ
 }
 
 
index 12dc0f98be45d120b840c3c418cdf42112d3a876..22212bff52d4a1cdfaee4c98496f4a29956558e0 100644 (file)
@@ -355,6 +355,7 @@ error:
     return -1;
 }
 
+
 static void
 dump_config(void)
 {
@@ -468,10 +469,30 @@ static int test_init_from_config(void)
     Py_SetProgramName(L"./globalvar");
     config.program_name = L"./conf_program_name";
 
-    /* FIXME: test argc/argv */
+    static wchar_t* argv[2] = {
+        L"-c",
+        L"pass",
+    };
+    config.argc = Py_ARRAY_LENGTH(argv);
+    config.argv = argv;
+
     config.program = L"conf_program";
-    /* FIXME: test xoptions */
-    /* FIXME: test warnoptions */
+
+    static wchar_t* xoptions[3] = {
+        L"core_xoption1=3",
+        L"core_xoption2=",
+        L"core_xoption3",
+    };
+    config.nxoption = Py_ARRAY_LENGTH(xoptions);
+    config.xoptions = xoptions;
+
+    static wchar_t* warnoptions[2] = {
+        L"default",
+        L"error::ResourceWarning",
+    };
+    config.nwarnoption = Py_ARRAY_LENGTH(warnoptions);
+    config.warnoptions = warnoptions;
+
     /* FIXME: test module_search_path_env */
     /* FIXME: test home */
     /* FIXME: test path config: module_search_path .. dll_path */
@@ -512,6 +533,11 @@ static int test_init_from_config(void)
 
     putenv("PYTHONIOENCODING=cp424");
     Py_SetStandardStreamEncoding("ascii", "ignore");
+#ifdef MS_WINDOWS
+    /* Py_SetStandardStreamEncoding() sets Py_LegacyWindowsStdioFlag to 1.
+       Force it to 0 through the config. */
+    config.legacy_windows_stdio = 0;
+#endif
     config.stdio_encoding = "iso8859-1";
     config.stdio_errors = "replace";
 
index 3e547c53ab01ad39ead04e5492303a4bb749f9c0..a040a865ae1ddb1fc6e4e84a4e48e0d17df2fd1b 100644 (file)
@@ -1444,14 +1444,6 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config)
         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); \
@@ -1464,117 +1456,81 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config)
                 goto fail; \
             } \
         } while (0)
-
-    SET_ITEM("install_signal_handlers",
-             PyLong_FromLong(config->install_signal_handlers));
-    SET_ITEM("use_environment",
-             PyLong_FromLong(config->use_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("filesystem_encoding",
-             FROM_STRING(config->filesystem_encoding));
-    SET_ITEM("filesystem_errors",
-             FROM_STRING(config->filesystem_errors));
-    SET_ITEM("stdio_encoding",
-             FROM_STRING(config->stdio_encoding));
-    SET_ITEM("utf8_mode",
-             PyLong_FromLong(config->utf8_mode));
-    SET_ITEM("pycache_prefix",
-             FROM_WSTRING(config->pycache_prefix));
-    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));
+#define FROM_STRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromString(STR) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_INT(ATTR) \
+    SET_ITEM(#ATTR, PyLong_FromLong(config->ATTR))
+#define SET_ITEM_UINT(ATTR) \
+    SET_ITEM(#ATTR, PyLong_FromUnsignedLong(config->ATTR))
+#define SET_ITEM_STR(ATTR) \
+    SET_ITEM(#ATTR, FROM_STRING(config->ATTR))
+#define FROM_WSTRING(STR) \
+    ((STR != NULL) ? \
+        PyUnicode_FromWideChar(STR, -1) \
+        : (Py_INCREF(Py_None), Py_None))
+#define SET_ITEM_WSTR(ATTR) \
+    SET_ITEM(#ATTR, FROM_WSTRING(config->ATTR))
+#define SET_ITEM_WSTRLIST(NOPTION, OPTIONS) \
+    SET_ITEM(#OPTIONS, _Py_wstrlist_as_pylist(config->NOPTION, config->OPTIONS))
+
+    SET_ITEM_INT(install_signal_handlers);
+    SET_ITEM_INT(use_environment);
+    SET_ITEM_INT(use_hash_seed);
+    SET_ITEM_UINT(hash_seed);
+    SET_ITEM_STR(allocator);
+    SET_ITEM_INT(dev_mode);
+    SET_ITEM_INT(faulthandler);
+    SET_ITEM_INT(tracemalloc);
+    SET_ITEM_INT(import_time);
+    SET_ITEM_INT(show_ref_count);
+    SET_ITEM_INT(show_alloc_count);
+    SET_ITEM_INT(dump_refs);
+    SET_ITEM_INT(malloc_stats);
+    SET_ITEM_INT(coerce_c_locale);
+    SET_ITEM_INT(coerce_c_locale_warn);
+    SET_ITEM_STR(filesystem_encoding);
+    SET_ITEM_STR(filesystem_errors);
+    SET_ITEM_INT(utf8_mode);
+    SET_ITEM_WSTR(pycache_prefix);
+    SET_ITEM_WSTR(program_name);
+    SET_ITEM_WSTRLIST(argc, argv);
+    SET_ITEM_WSTR(program);
+    SET_ITEM_WSTRLIST(nxoption, xoptions);
+    SET_ITEM_WSTRLIST(nwarnoption, warnoptions);
+    SET_ITEM_WSTR(module_search_path_env);
+    SET_ITEM_WSTR(home);
+    SET_ITEM_WSTRLIST(nmodule_search_path, module_search_paths);
+    SET_ITEM_WSTR(executable);
+    SET_ITEM_WSTR(prefix);
+    SET_ITEM_WSTR(base_prefix);
+    SET_ITEM_WSTR(exec_prefix);
+    SET_ITEM_WSTR(base_exec_prefix);
 #ifdef MS_WINDOWS
-    SET_ITEM("dll_path",
-             FROM_WSTRING(config->dll_path));
+    SET_ITEM_WSTR(dll_path);
 #endif
-    SET_ITEM("isolated",
-             PyLong_FromLong(config->isolated));
-    SET_ITEM("site_import",
-             PyLong_FromLong(config->site_import));
-    SET_ITEM("bytes_warning",
-             PyLong_FromLong(config->bytes_warning));
-    SET_ITEM("inspect",
-             PyLong_FromLong(config->inspect));
-    SET_ITEM("interactive",
-             PyLong_FromLong(config->interactive));
-    SET_ITEM("optimization_level",
-             PyLong_FromLong(config->optimization_level));
-    SET_ITEM("parser_debug",
-             PyLong_FromLong(config->parser_debug));
-    SET_ITEM("write_bytecode",
-             PyLong_FromLong(config->write_bytecode));
-    SET_ITEM("verbose",
-             PyLong_FromLong(config->verbose));
-    SET_ITEM("quiet",
-             PyLong_FromLong(config->quiet));
-    SET_ITEM("user_site_directory",
-             PyLong_FromLong(config->user_site_directory));
-    SET_ITEM("buffered_stdio",
-             PyLong_FromLong(config->buffered_stdio));
-    SET_ITEM("stdio_encoding",
-             FROM_STRING(config->stdio_encoding));
-    SET_ITEM("stdio_errors",
-             FROM_STRING(config->stdio_errors));
+    SET_ITEM_INT(isolated);
+    SET_ITEM_INT(site_import);
+    SET_ITEM_INT(bytes_warning);
+    SET_ITEM_INT(inspect);
+    SET_ITEM_INT(interactive);
+    SET_ITEM_INT(optimization_level);
+    SET_ITEM_INT(parser_debug);
+    SET_ITEM_INT(write_bytecode);
+    SET_ITEM_INT(verbose);
+    SET_ITEM_INT(quiet);
+    SET_ITEM_INT(user_site_directory);
+    SET_ITEM_INT(buffered_stdio);
+    SET_ITEM_STR(stdio_encoding);
+    SET_ITEM_STR(stdio_errors);
 #ifdef MS_WINDOWS
-    SET_ITEM("legacy_windows_fs_encoding",
-             PyLong_FromLong(config->legacy_windows_fs_encoding));
-    SET_ITEM("legacy_windows_stdio",
-             PyLong_FromLong(config->legacy_windows_stdio));
+    SET_ITEM_INT(legacy_windows_fs_encoding);
+    SET_ITEM_INT(legacy_windows_stdio);
 #endif
-    SET_ITEM("_install_importlib",
-             PyLong_FromLong(config->_install_importlib));
-    SET_ITEM("_check_hash_pycs_mode",
-             FROM_STRING(config->_check_hash_pycs_mode));
-    SET_ITEM("_frozen",
-             PyLong_FromLong(config->_frozen));
+    SET_ITEM_INT(_install_importlib);
+    SET_ITEM_STR(_check_hash_pycs_mode);
+    SET_ITEM_INT(_frozen);
 
     return dict;
 
@@ -1585,4 +1541,9 @@ fail:
 #undef FROM_STRING
 #undef FROM_WSTRING
 #undef SET_ITEM
+#undef SET_ITEM_INT
+#undef SET_ITEM_UINT
+#undef SET_ITEM_STR
+#undef SET_ITEM_WSTR
+#undef SET_ITEM_WSTRLIST
 }