[Python-checkins] bpo-29778: test_embed tests the path configuration (GH-21306)

Victor Stinner webhook-mailer at python.org
Tue Jul 7 18:20:45 EDT 2020


https://github.com/python/cpython/commit/8f42748ded5e978fe8a924115179d45a74a6363b
commit: 8f42748ded5e978fe8a924115179d45a74a6363b
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-07-08T00:20:37+02:00
summary:

bpo-29778: test_embed tests the path configuration (GH-21306)

files:
M Include/internal/pycore_pathconfig.h
M Lib/test/test_embed.py
M Modules/_testinternalcapi.c
M Python/initconfig.c
M Python/pathconfig.c

diff --git a/Include/internal/pycore_pathconfig.h b/Include/internal/pycore_pathconfig.h
index 42d61b1ca26bd..15447f54490fb 100644
--- a/Include/internal/pycore_pathconfig.h
+++ b/Include/internal/pycore_pathconfig.h
@@ -65,6 +65,7 @@ extern wchar_t* _Py_GetDLLPath(void);
 
 extern PyStatus _PyConfig_WritePathConfig(const PyConfig *config);
 extern void _Py_DumpPathConfig(PyThreadState *tstate);
+extern PyObject* _PyPathConfig_AsDict(void);
 
 #ifdef __cplusplus
 }
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 44d2596d9eb30..2b740521e723c 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -471,6 +471,31 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             ('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
         ))
 
+    # path config
+    if MS_WINDOWS:
+        PATH_CONFIG = {
+            'isolated': -1,
+            'site_import': -1,
+            'python3_dll': GET_DEFAULT_CONFIG,
+        }
+    else:
+        PATH_CONFIG = {}
+    # other keys are copied by COPY_PATH_CONFIG
+
+    COPY_PATH_CONFIG = [
+        # Copy core config to global config for expected values
+        'prefix',
+        'exec_prefix',
+        'program_name',
+        'home',
+        # program_full_path and module_search_path are copied indirectly from
+        # the core configuration in check_path_config().
+    ]
+    if MS_WINDOWS:
+        COPY_PATH_CONFIG.extend((
+            'base_executable',
+        ))
+
     EXPECTED_CONFIG = None
 
     @classmethod
@@ -535,7 +560,8 @@ def _get_expected_config(self):
             configs[config_key] = config
         return configs
 
-    def get_expected_config(self, expected_preconfig, expected, env, api,
+    def get_expected_config(self, expected_preconfig, expected,
+                            expected_pathconfig, env, api,
                             modify_path_cb=None):
         cls = self.__class__
         configs = self._get_expected_config()
@@ -545,6 +571,11 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
             if value is self.GET_DEFAULT_CONFIG:
                 expected_preconfig[key] = pre_config[key]
 
+        path_config = configs['path_config']
+        for key, value in expected_pathconfig.items():
+            if value is self.GET_DEFAULT_CONFIG:
+                expected_pathconfig[key] = path_config[key]
+
         if not expected_preconfig['configure_locale'] or api == API_COMPAT:
             # there is no easy way to get the locale encoding before
             # setlocale(LC_CTYPE, "") is called: don't test encodings
@@ -637,8 +668,19 @@ def check_global_config(self, configs):
 
         self.assertEqual(configs['global_config'], expected)
 
+    def check_path_config(self, configs, expected):
+        config = configs['config']
+
+        for key in self.COPY_PATH_CONFIG:
+            expected[key] = config[key]
+        expected['module_search_path'] = os.path.pathsep.join(config['module_search_paths'])
+        expected['program_full_path'] = config['executable']
+
+        self.assertEqual(configs['path_config'], expected)
+
     def check_all_configs(self, testname, expected_config=None,
-                          expected_preconfig=None, modify_path_cb=None,
+                          expected_preconfig=None, expected_pathconfig=None,
+                          modify_path_cb=None,
                           stderr=None, *, api, preconfig_api=None,
                           env=None, ignore_stderr=False, cwd=None):
         new_env = remove_python_envvars()
@@ -657,9 +699,14 @@ def check_all_configs(self, testname, expected_config=None,
         if expected_preconfig is None:
             expected_preconfig = {}
         expected_preconfig = dict(default_preconfig, **expected_preconfig)
+
         if expected_config is None:
             expected_config = {}
 
+        if expected_pathconfig is None:
+            expected_pathconfig = {}
+        expected_pathconfig = dict(self.PATH_CONFIG, **expected_pathconfig)
+
         if api == API_PYTHON:
             default_config = self.CONFIG_PYTHON
         elif api == API_ISOLATED:
@@ -669,7 +716,9 @@ def check_all_configs(self, testname, expected_config=None,
         expected_config = dict(default_config, **expected_config)
 
         self.get_expected_config(expected_preconfig,
-                                 expected_config, env,
+                                 expected_config,
+                                 expected_pathconfig,
+                                 env,
                                  api, modify_path_cb)
 
         out, err = self.run_embedded_interpreter(testname,
@@ -686,6 +735,7 @@ def check_all_configs(self, testname, expected_config=None,
         self.check_pre_config(configs, expected_preconfig)
         self.check_config(configs, expected_config)
         self.check_global_config(configs)
+        self.check_path_config(configs, expected_pathconfig)
         return configs
 
     def test_init_default_config(self):
@@ -1258,22 +1308,24 @@ def test_init_pyvenv_cfg(self):
                 'executable': executable,
                 'module_search_paths': paths,
             }
+            path_config = {}
             if MS_WINDOWS:
                 config['base_prefix'] = pyvenv_home
                 config['prefix'] = pyvenv_home
-            env = self.copy_paths_by_env(config)
-            actual = self.check_all_configs("test_init_compat_config", config,
-                                            api=API_COMPAT, env=env,
-                                            ignore_stderr=True, cwd=tmpdir)
-            if MS_WINDOWS:
-                self.assertEqual(
-                    actual['windows']['python3_dll'],
-                    os.path.join(
-                        tmpdir,
-                        os.path.basename(self.EXPECTED_CONFIG['windows']['python3_dll'])
-                    )
-                )
 
+                ver = sys.version_info
+                dll = f'python{ver.major}'
+                if debug_build(executable):
+                    dll += '_d'
+                dll += '.DLL'
+                dll = os.path.join(os.path.dirname(executable), dll)
+                path_config['python3_dll'] = dll
+
+            env = self.copy_paths_by_env(config)
+            self.check_all_configs("test_init_compat_config", config,
+                                   expected_pathconfig=path_config,
+                                   api=API_COMPAT, env=env,
+                                   ignore_stderr=True, cwd=tmpdir)
 
     def test_global_pathconfig(self):
         # Test C API functions getting the path configuration:
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index f08bf8d83d474..ad74af8363ef4 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -18,53 +18,10 @@
 #include "pycore_gc.h"           // PyGC_Head
 
 
-#ifdef MS_WINDOWS
-#include <windows.h>
-
-static int
-_add_windows_config(PyObject *configs)
-{
-    HMODULE hPython3;
-    wchar_t py3path[MAX_PATH];
-    PyObject *dict = PyDict_New();
-    PyObject *obj = NULL;
-    if (!dict) {
-        return -1;
-    }
-
-    hPython3 = GetModuleHandleW(PY3_DLLNAME);
-    if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) {
-        obj = PyUnicode_FromWideChar(py3path, -1);
-    } else {
-        obj = Py_None;
-        Py_INCREF(obj);
-    }
-    if (obj &&
-        !PyDict_SetItemString(dict, "python3_dll", obj) &&
-        !PyDict_SetItemString(configs, "windows", dict)) {
-        Py_DECREF(obj);
-        Py_DECREF(dict);
-        return 0;
-    }
-    Py_DECREF(obj);
-    Py_DECREF(dict);
-    return -1;
-}
-#endif
-
-
 static PyObject *
 get_configs(PyObject *self, PyObject *Py_UNUSED(args))
 {
-    PyObject *dict = _Py_GetConfigsAsDict();
-#ifdef MS_WINDOWS
-    if (dict) {
-        if (_add_windows_config(dict) < 0) {
-            Py_CLEAR(dict);
-        }
-    }
-#endif
-    return dict;
+    return _Py_GetConfigsAsDict();
 }
 
 
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 86285c77e2307..64286763b621e 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -868,9 +868,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
 static PyObject *
 config_as_dict(const PyConfig *config)
 {
-    PyObject *dict;
-
-    dict = PyDict_New();
+    PyObject *dict = PyDict_New();
     if (dict == NULL) {
         return NULL;
     }
@@ -2643,6 +2641,16 @@ _Py_GetConfigsAsDict(void)
     }
     Py_CLEAR(dict);
 
+    /* path config */
+    dict = _PyPathConfig_AsDict();
+    if (dict == NULL) {
+        goto error;
+    }
+    if (PyDict_SetItemString(result, "path_config", dict) < 0) {
+        goto error;
+    }
+    Py_CLEAR(dict);
+
     return result;
 
 error:
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 9a302213e77b6..12a684a66b718 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -186,6 +186,80 @@ pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
     return status;
 }
 
+PyObject *
+_PyPathConfig_AsDict(void)
+{
+    PyObject *dict = PyDict_New();
+    if (dict == NULL) {
+        return NULL;
+    }
+
+#define SET_ITEM(KEY, EXPR) \
+        do { \
+            PyObject *obj = (EXPR); \
+            if (obj == NULL) { \
+                goto fail; \
+            } \
+            int res = PyDict_SetItemString(dict, KEY, obj); \
+            Py_DECREF(obj); \
+            if (res < 0) { \
+                goto fail; \
+            } \
+        } while (0)
+#define SET_ITEM_STR(KEY) \
+        SET_ITEM(#KEY, \
+            (_Py_path_config.KEY \
+             ? PyUnicode_FromWideChar(_Py_path_config.KEY, -1) \
+             : (Py_INCREF(Py_None), Py_None)))
+#define SET_ITEM_INT(KEY) \
+        SET_ITEM(#KEY, PyLong_FromLong(_Py_path_config.KEY))
+
+    SET_ITEM_STR(program_full_path);
+    SET_ITEM_STR(prefix);
+    SET_ITEM_STR(exec_prefix);
+    SET_ITEM_STR(module_search_path);
+    SET_ITEM_STR(program_name);
+    SET_ITEM_STR(home);
+#ifdef MS_WINDOWS
+    SET_ITEM_INT(isolated);
+    SET_ITEM_INT(site_import);
+    SET_ITEM_STR(base_executable);
+
+    {
+        wchar_t py3path[MAX_PATH];
+        HMODULE hPython3 = GetModuleHandleW(PY3_DLLNAME);
+        PyObject *obj;
+        if (hPython3
+            && GetModuleFileNameW(hPython3, py3path, Py_ARRAY_LENGTH(py3path)))
+        {
+            obj = PyUnicode_FromWideChar(py3path, -1);
+            if (obj == NULL) {
+                goto fail;
+            }
+        }
+        else {
+            obj = Py_None;
+            Py_INCREF(obj);
+        }
+        if (PyDict_SetItemString(dict, "python3_dll", obj) < 0) {
+            Py_DECREF(obj);
+            goto fail;
+        }
+        Py_DECREF(obj);
+    }
+#endif
+
+#undef SET_ITEM
+#undef SET_ITEM_STR
+#undef SET_ITEM_INT
+
+    return dict;
+
+fail:
+    Py_DECREF(dict);
+    return NULL;
+}
+
 
 PyStatus
 _PyConfig_WritePathConfig(const PyConfig *config)



More information about the Python-checkins mailing list