[Python-checkins] bpo-38234: Fix _PyConfig_InitPathConfig() (GH-16335) (GH-16336)

Victor Stinner webhook-mailer at python.org
Mon Sep 23 13:50:33 EDT 2019


https://github.com/python/cpython/commit/3f5409a3f13c59baa34656bccefdc3728e46c9ef
commit: 3f5409a3f13c59baa34656bccefdc3728e46c9ef
branch: 3.8
author: Victor Stinner <vstinner at redhat.com>
committer: GitHub <noreply at github.com>
date: 2019-09-23T19:50:27+02:00
summary:

bpo-38234: Fix _PyConfig_InitPathConfig() (GH-16335) (GH-16336)

* _PyConfig_InitPathConfig() now starts by copying the global path
  configuration, and then override values set in PyConfig.
* _PyPathConfig_Calculate() implementations no longer override
  _PyPathConfig fields which are already computed. For example,
  if _PyPathConfig.prefix is not NULL, leave it unchanged.
* If Py_SetPath() has been called, _PyConfig_InitPathConfig() doesn't
  call _PyPathConfig_Calculate() anymore.
* _PyPathConfig_Calculate() no longer uses PyConfig,
  except to initialize PyCalculatePath structure.
* pathconfig_calculate(): remove useless temporary
  "_PyPathConfig new_config" variable.
* calculate_module_search_path(): remove hack to workaround memory
  allocation failure, call Py_FatalError() instead.
* Fix get_program_full_path(): handle memory allocation failure.

(cherry picked from commit 9c42f8cda552694f3b47d6388d4ae84d61731872)

files:
M Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst
M Modules/getpath.c
M PC/getpathp.c
M Python/pathconfig.c

diff --git a/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst b/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst
index 6310f19f004e..fe658ad75d89 100644
--- a/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst	
+++ b/Misc/NEWS.d/next/C API/2019-09-20-17-22-41.bpo-38234.ZbquVK.rst	
@@ -1,2 +1,3 @@
-Python ignored path passed to :c:func:`Py_SetPath`, fix Python
-initialization to use the specified path.
+Python ignored arguments passed to :c:func:`Py_SetPath`,
+:c:func:`Py_SetPythonHome` and :c:func:`Py_SetProgramName`: fix Python
+initialization to use specified arguments.
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 5afb5b152638..f60fe3c015bb 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -133,6 +133,9 @@ typedef struct {
 
     int prefix_found;         /* found platform independent libraries? */
     int exec_prefix_found;    /* found the platform dependent libraries? */
+
+    int warnings;
+    const wchar_t *pythonpath_env;
 } PyCalculatePath;
 
 static const wchar_t delimiter[2] = {DELIM, '\0'};
@@ -365,17 +368,16 @@ add_exe_suffix(wchar_t *progpath, size_t progpathlen)
    bytes long.
 */
 static PyStatus
-search_for_prefix(const PyConfig *config, PyCalculatePath *calculate,
-                  wchar_t *prefix, size_t prefix_len,
-                  int *found)
+search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                  wchar_t *prefix, size_t prefix_len, int *found)
 {
     PyStatus status;
     size_t n;
     wchar_t *vpath;
 
     /* If PYTHONHOME is set, we believe it unconditionally */
-    if (config->home) {
-        if (safe_wcscpy(prefix, config->home, prefix_len) < 0) {
+    if (pathconfig->home) {
+        if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) {
             return PATHLEN_ERR();
         }
         wchar_t *delim = wcschr(prefix, DELIM);
@@ -482,19 +484,19 @@ search_for_prefix(const PyConfig *config, PyCalculatePath *calculate,
 
 
 static PyStatus
-calculate_prefix(const PyConfig *config,
-                 PyCalculatePath *calculate, wchar_t *prefix, size_t prefix_len)
+calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                 wchar_t *prefix, size_t prefix_len)
 {
     PyStatus status;
 
-    status = search_for_prefix(config, calculate, prefix, prefix_len,
-                            &calculate->prefix_found);
+    status = search_for_prefix(calculate, pathconfig, prefix, prefix_len,
+                               &calculate->prefix_found);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
     if (!calculate->prefix_found) {
-        if (config->pathconfig_warnings) {
+        if (calculate->warnings) {
             fprintf(stderr,
                 "Could not find platform independent libraries <prefix>\n");
         }
@@ -544,8 +546,7 @@ calculate_reduce_prefix(PyCalculatePath *calculate,
    MAXPATHLEN bytes long.
 */
 static PyStatus
-search_for_exec_prefix(const PyConfig *config,
-                       PyCalculatePath *calculate,
+search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
                        wchar_t *exec_prefix, size_t exec_prefix_len,
                        int *found)
 {
@@ -553,15 +554,15 @@ search_for_exec_prefix(const PyConfig *config,
     size_t n;
 
     /* If PYTHONHOME is set, we believe it unconditionally */
-    if (config->home) {
-        wchar_t *delim = wcschr(config->home, DELIM);
+    if (pathconfig->home) {
+        wchar_t *delim = wcschr(pathconfig->home, DELIM);
         if (delim) {
             if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) {
                 return PATHLEN_ERR();
             }
         }
         else {
-            if (safe_wcscpy(exec_prefix, config->home, exec_prefix_len) < 0) {
+            if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) {
                 return PATHLEN_ERR();
             }
         }
@@ -668,21 +669,20 @@ search_for_exec_prefix(const PyConfig *config,
 
 
 static PyStatus
-calculate_exec_prefix(const PyConfig *config,
-                      PyCalculatePath *calculate,
+calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
                       wchar_t *exec_prefix, size_t exec_prefix_len)
 {
     PyStatus status;
 
-    status = search_for_exec_prefix(config, calculate,
-                                 exec_prefix, exec_prefix_len,
-                                 &calculate->exec_prefix_found);
+    status = search_for_exec_prefix(calculate, pathconfig,
+                                    exec_prefix, exec_prefix_len,
+                                    &calculate->exec_prefix_found);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
     if (!calculate->exec_prefix_found) {
-        if (config->pathconfig_warnings) {
+        if (calculate->warnings) {
             fprintf(stderr,
                 "Could not find platform dependent libraries <exec_prefix>\n");
         }
@@ -721,8 +721,7 @@ calculate_reduce_exec_prefix(PyCalculatePath *calculate,
 
 
 static PyStatus
-calculate_program_full_path(const PyConfig *config,
-                            PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
 {
     PyStatus status;
     wchar_t program_full_path[MAXPATHLEN + 1];
@@ -743,8 +742,8 @@ calculate_program_full_path(const PyConfig *config,
      * other way to find a directory to start the search from.  If
      * $PATH isn't exported, you lose.
      */
-    if (wcschr(config->program_name, SEP)) {
-        if (safe_wcscpy(program_full_path, config->program_name,
+    if (wcschr(pathconfig->program_name, SEP)) {
+        if (safe_wcscpy(program_full_path, pathconfig->program_name,
                         program_full_path_len) < 0) {
             return PATHLEN_ERR();
         }
@@ -795,8 +794,8 @@ calculate_program_full_path(const PyConfig *config,
                 }
             }
 
-            status = joinpath(program_full_path, config->program_name,
-                            program_full_path_len);
+            status = joinpath(program_full_path, pathconfig->program_name,
+                              program_full_path_len);
             if (_PyStatus_EXCEPTION(status)) {
                 return status;
             }
@@ -1030,15 +1029,14 @@ calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
 
 
 static PyStatus
-calculate_module_search_path(const PyConfig *config,
-                             PyCalculatePath *calculate,
-                             const wchar_t *prefix, const wchar_t *exec_prefix,
-                             _PyPathConfig *pathconfig)
+calculate_module_search_path(PyCalculatePath *calculate,
+                             _PyPathConfig *pathconfig,
+                             const wchar_t *prefix, const wchar_t *exec_prefix)
 {
     /* Calculate size of return buffer */
     size_t bufsz = 0;
-    if (config->pythonpath_env != NULL) {
-        bufsz += wcslen(config->pythonpath_env) + 1;
+    if (calculate->pythonpath_env != NULL) {
+        bufsz += wcslen(calculate->pythonpath_env) + 1;
     }
 
     wchar_t *defpath = calculate->pythonpath;
@@ -1072,8 +1070,8 @@ calculate_module_search_path(const PyConfig *config,
     buf[0] = '\0';
 
     /* Run-time value of $PYTHONPATH goes first */
-    if (config->pythonpath_env) {
-        wcscpy(buf, config->pythonpath_env);
+    if (calculate->pythonpath_env) {
+        wcscpy(buf, calculate->pythonpath_env);
         wcscat(buf, delimiter);
     }
 
@@ -1149,6 +1147,10 @@ calculate_init(PyCalculatePath *calculate,
     if (!calculate->lib_python) {
         return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
     }
+
+    calculate->warnings = config->pathconfig_warnings;
+    calculate->pythonpath_env = config->pythonpath_env;
+
     return _PyStatus_OK();
 }
 
@@ -1165,14 +1167,15 @@ calculate_free(PyCalculatePath *calculate)
 
 
 static PyStatus
-calculate_path_impl(const PyConfig *config,
-                    PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
 {
     PyStatus status;
 
-    status = calculate_program_full_path(config, calculate, pathconfig);
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
+    if (pathconfig->program_full_path == NULL) {
+        status = calculate_program_full_path(calculate, pathconfig);
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
     }
 
     status = calculate_argv0_path(calculate, pathconfig->program_full_path);
@@ -1187,8 +1190,8 @@ calculate_path_impl(const PyConfig *config,
 
     wchar_t prefix[MAXPATHLEN+1];
     memset(prefix, 0, sizeof(prefix));
-    status = calculate_prefix(config, calculate,
-                           prefix, Py_ARRAY_LENGTH(prefix));
+    status = calculate_prefix(calculate, pathconfig,
+                              prefix, Py_ARRAY_LENGTH(prefix));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
@@ -1200,52 +1203,65 @@ calculate_path_impl(const PyConfig *config,
 
     wchar_t exec_prefix[MAXPATHLEN+1];
     memset(exec_prefix, 0, sizeof(exec_prefix));
-    status = calculate_exec_prefix(config, calculate,
-                                exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
+    status = calculate_exec_prefix(calculate, pathconfig,
+                                   exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
     if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
-        config->pathconfig_warnings)
+        calculate->warnings)
     {
         fprintf(stderr,
                 "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
     }
 
     if (pathconfig->module_search_path == NULL) {
-        status = calculate_module_search_path(config, calculate,
-                                              prefix, exec_prefix, pathconfig);
+        status = calculate_module_search_path(calculate, pathconfig,
+                                              prefix, exec_prefix);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
     }
 
-    status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix));
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
-    }
-
-    pathconfig->prefix = _PyMem_RawWcsdup(prefix);
     if (pathconfig->prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
-    }
+        status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix));
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
 
-    status = calculate_reduce_exec_prefix(calculate,
-                                       exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
-    if (_PyStatus_EXCEPTION(status)) {
-        return status;
+        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
+        if (pathconfig->prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
 
-    pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
     if (pathconfig->exec_prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
+        status = calculate_reduce_exec_prefix(calculate,
+                                              exec_prefix,
+                                              Py_ARRAY_LENGTH(exec_prefix));
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
+
+        pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
+        if (pathconfig->exec_prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
 
     return _PyStatus_OK();
 }
 
 
+/* Calculate 'pathconfig' attributes:
+
+   - program_full_path
+   - module_search_path
+   - prefix
+   - exec_prefix
+
+   If an attribute is already set (non NULL), it is left unchanged. */
 PyStatus
 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
 {
@@ -1258,7 +1274,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
         goto done;
     }
 
-    status = calculate_path_impl(config, &calculate, pathconfig);
+    status = calculate_path(&calculate, pathconfig);
     if (_PyStatus_EXCEPTION(status)) {
         goto done;
     }
diff --git a/PC/getpathp.c b/PC/getpathp.c
index 888bef1146fd..5670fa2ee4e1 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -130,6 +130,8 @@ typedef struct {
     wchar_t zip_path[MAXPATHLEN+1];
 
     wchar_t *dll_path;
+
+    const wchar_t *pythonpath_env;
 } PyCalculatePath;
 
 
@@ -322,7 +324,7 @@ gotlandmark(wchar_t *prefix, const wchar_t *landmark)
 
 
 /* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd.
-   assumption provided by only caller, calculate_path_impl() */
+   assumption provided by only caller, calculate_path() */
 static int
 search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path, const wchar_t *landmark)
 {
@@ -534,6 +536,7 @@ _Py_GetDLLPath(void)
 static PyStatus
 get_program_full_path(_PyPathConfig *pathconfig)
 {
+    PyStatus status;
     const wchar_t *pyvenv_launcher;
     wchar_t program_full_path[MAXPATHLEN+1];
     memset(program_full_path, 0, sizeof(program_full_path));
@@ -548,12 +551,18 @@ get_program_full_path(_PyPathConfig *pathconfig)
     pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
     if (pyvenv_launcher && pyvenv_launcher[0]) {
         /* If overridden, preserve the original full path */
-        pathconfig->base_executable = PyMem_RawMalloc(
-            sizeof(wchar_t) * (MAXPATHLEN + 1));
-        PyStatus status = canonicalize(pathconfig->base_executable,
-                                       program_full_path);
-        if (_PyStatus_EXCEPTION(status)) {
-            return status;
+        if (pathconfig->base_executable == NULL) {
+            pathconfig->base_executable = PyMem_RawMalloc(
+                sizeof(wchar_t) * (MAXPATHLEN + 1));
+            if (pathconfig->base_executable == NULL) {
+                return _PyStatus_NO_MEMORY();
+            }
+
+            status = canonicalize(pathconfig->base_executable,
+                                  program_full_path);
+            if (_PyStatus_EXCEPTION(status)) {
+                return status;
+            }
         }
 
         wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
@@ -562,11 +571,20 @@ get_program_full_path(_PyPathConfig *pathconfig)
         _wputenv_s(L"__PYVENV_LAUNCHER__", L"");
     }
 
-    pathconfig->program_full_path = PyMem_RawMalloc(
-        sizeof(wchar_t) * (MAXPATHLEN + 1));
+    if (pathconfig->program_full_path == NULL) {
+        pathconfig->program_full_path = PyMem_RawMalloc(
+            sizeof(wchar_t) * (MAXPATHLEN + 1));
+        if (pathconfig->program_full_path == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
 
-    return canonicalize(pathconfig->program_full_path,
-                        program_full_path);
+        status = canonicalize(pathconfig->program_full_path,
+                              program_full_path);
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
+    }
+    return _PyStatus_OK();
 }
 
 
@@ -657,7 +675,13 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
     }
 
     fclose(sp_file);
-    pathconfig->module_search_path = buf;
+    if (pathconfig->module_search_path == NULL) {
+        pathconfig->module_search_path = _PyMem_RawWcsdup(buf);
+        if (pathconfig->module_search_path == NULL) {
+            Py_FatalError("out of memory");
+        }
+    }
+    PyMem_RawFree(buf);
     return 1;
 
 error:
@@ -668,8 +692,7 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
 
 
 static PyStatus
-calculate_init(PyCalculatePath *calculate,
-               const PyConfig *config)
+calculate_init(PyCalculatePath *calculate, const PyConfig *config)
 {
     calculate->home = config->home;
     calculate->path_env = _wgetenv(L"PATH");
@@ -679,6 +702,8 @@ calculate_init(PyCalculatePath *calculate,
         return _PyStatus_NO_MEMORY();
     }
 
+    calculate->pythonpath_env = config->pythonpath_env;
+
     return _PyStatus_OK();
 }
 
@@ -785,8 +810,8 @@ calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix)
 
 
 static PyStatus
-calculate_module_search_path(const PyConfig *config,
-                             PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+calculate_module_search_path(PyCalculatePath *calculate,
+                             _PyPathConfig *pathconfig,
                              wchar_t *prefix)
 {
     int skiphome = calculate->home==NULL ? 0 : 1;
@@ -796,7 +821,7 @@ calculate_module_search_path(const PyConfig *config,
 #endif
     /* We only use the default relative PYTHONPATH if we haven't
        anything better to use! */
-    int skipdefault = (config->pythonpath_env != NULL ||
+    int skipdefault = (calculate->pythonpath_env != NULL ||
                        calculate->home != NULL ||
                        calculate->machine_path != NULL ||
                        calculate->user_path != NULL);
@@ -835,30 +860,20 @@ calculate_module_search_path(const PyConfig *config,
         bufsz += wcslen(calculate->machine_path) + 1;
     }
     bufsz += wcslen(calculate->zip_path) + 1;
-    if (config->pythonpath_env != NULL) {
-        bufsz += wcslen(config->pythonpath_env) + 1;
+    if (calculate->pythonpath_env != NULL) {
+        bufsz += wcslen(calculate->pythonpath_env) + 1;
     }
 
     wchar_t *buf, *start_buf;
     buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
     if (buf == NULL) {
-        /* We can't exit, so print a warning and limp along */
-        fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n");
-        if (config->pythonpath_env) {
-            fprintf(stderr, "Using environment $PYTHONPATH.\n");
-            pathconfig->module_search_path = config->pythonpath_env;
-        }
-        else {
-            fprintf(stderr, "Using default static path.\n");
-            pathconfig->module_search_path = PYTHONPATH;
-        }
-        return _PyStatus_OK();
+        Py_FatalError("Can't malloc dynamic PYTHONPATH");
     }
     start_buf = buf;
 
-    if (config->pythonpath_env) {
+    if (calculate->pythonpath_env) {
         if (wcscpy_s(buf, bufsz - (buf - start_buf),
-                     config->pythonpath_env)) {
+                     calculate->pythonpath_env)) {
             return INIT_ERR_BUFFER_OVERFLOW();
         }
         buf = wcschr(buf, L'\0');
@@ -971,8 +986,7 @@ calculate_module_search_path(const PyConfig *config,
 
 
 static PyStatus
-calculate_path_impl(const PyConfig *config,
-                    PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
 {
     PyStatus status;
 
@@ -1003,21 +1017,24 @@ calculate_path_impl(const PyConfig *config,
     calculate_home_prefix(calculate, prefix);
 
     if (pathconfig->module_search_path == NULL) {
-        status = calculate_module_search_path(config, calculate,
-                                              pathconfig, prefix);
+        status = calculate_module_search_path(calculate, pathconfig, prefix);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
     }
 
 done:
-    pathconfig->prefix = _PyMem_RawWcsdup(prefix);
     if (pathconfig->prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
+        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
+        if (pathconfig->prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
-    pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix);
     if (pathconfig->exec_prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
+        pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix);
+        if (pathconfig->exec_prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
 
     return _PyStatus_OK();
@@ -1033,6 +1050,17 @@ calculate_free(PyCalculatePath *calculate)
 }
 
 
+/* Calculate 'pathconfig' attributes:
+
+   - base_executable
+   - program_full_path
+   - module_search_path
+   - prefix
+   - exec_prefix
+   - isolated
+   - site_import
+
+   If an attribute is already set (non NULL), it is left unchanged. */
 PyStatus
 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
 {
@@ -1045,7 +1073,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
         goto done;
     }
 
-    status = calculate_path_impl(config, &calculate, pathconfig);
+    status = calculate_path(&calculate, pathconfig);
 
 done:
     calculate_free(&calculate);
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 1934f2e1c4b8..09533100b463 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -51,12 +51,12 @@ pathconfig_clear(_PyPathConfig *config)
         ATTR = NULL; \
     } while (0)
 
-    CLEAR(config->prefix);
     CLEAR(config->program_full_path);
+    CLEAR(config->prefix);
     CLEAR(config->exec_prefix);
     CLEAR(config->module_search_path);
-    CLEAR(config->home);
     CLEAR(config->program_name);
+    CLEAR(config->home);
     CLEAR(config->base_executable);
 #undef CLEAR
 
@@ -64,60 +64,31 @@ pathconfig_clear(_PyPathConfig *config)
 }
 
 
-/* Calculate the path configuration: initialize pathconfig from config */
 static PyStatus
-pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
+pathconfig_copy(_PyPathConfig *config, const _PyPathConfig *config2)
 {
-    PyStatus status;
-    _PyPathConfig new_config = _PyPathConfig_INIT;
-
-    PyMemAllocatorEx old_alloc;
-    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    pathconfig_clear(config);
 
-    if (copy_wstr(&new_config.module_search_path,
-                  _Py_path_config.module_search_path) < 0)
-    {
-        status = _PyStatus_NO_MEMORY();
-        goto error;
-    }
-
-    /* Calculate program_full_path, prefix, exec_prefix,
-       dll_path (Windows), and module_search_path */
-    status = _PyPathConfig_Calculate(&new_config, config);
-    if (_PyStatus_EXCEPTION(status)) {
-        goto error;
-    }
-
-    /* Copy home and program_name from config */
-    if (copy_wstr(&new_config.home, config->home) < 0) {
-        status = _PyStatus_NO_MEMORY();
-        goto error;
-    }
-    if (copy_wstr(&new_config.program_name, config->program_name) < 0) {
-        status = _PyStatus_NO_MEMORY();
-        goto error;
-    }
-    if (config->base_executable) {
-        PyMem_RawFree(new_config.base_executable);
-        if (copy_wstr(&new_config.base_executable,
-                      config->base_executable) < 0) {
-            status = _PyStatus_NO_MEMORY();
-            goto error;
-        }
-    }
-
-    pathconfig_clear(pathconfig);
-    *pathconfig = new_config;
+#define COPY_ATTR(ATTR) \
+    do { \
+        if (copy_wstr(&config->ATTR, config2->ATTR) < 0) { \
+            return _PyStatus_NO_MEMORY(); \
+        } \
+    } while (0)
 
-    status = _PyStatus_OK();
-    goto done;
+    COPY_ATTR(program_full_path);
+    COPY_ATTR(prefix);
+    COPY_ATTR(exec_prefix);
+    COPY_ATTR(module_search_path);
+    COPY_ATTR(program_name);
+    COPY_ATTR(home);
+    config->isolated = config2->isolated;
+    config->site_import = config2->site_import;
+    COPY_ATTR(base_executable);
 
-error:
-    pathconfig_clear(&new_config);
+#undef COPY_ATTR
 
-done:
-    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
-    return status;
+    return _PyStatus_OK();
 }
 
 
@@ -168,7 +139,7 @@ _PyWideStringList_Join(const PyWideStringList *list, wchar_t sep)
 }
 
 
-/* Set the global path configuration from config. */
+/* Initialize _Py_dll_path on Windows. Do nothing on other platforms. */
 PyStatus
 _PyPathConfig_Init(void)
 {
@@ -194,47 +165,39 @@ _PyPathConfig_Init(void)
 
 
 static PyStatus
-pathconfig_global_init_from_config(const PyConfig *config)
+pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
 {
     PyStatus status;
     PyMemAllocatorEx old_alloc;
     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 
-    _PyPathConfig pathconfig = _PyPathConfig_INIT;
-
-    pathconfig.module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM);
-    if (pathconfig.module_search_path == NULL) {
-        goto no_memory;
+    if (config->module_search_paths_set) {
+        pathconfig->module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM);
+        if (pathconfig->module_search_path == NULL) {
+            goto no_memory;
+        }
     }
 
-    if (copy_wstr(&pathconfig.program_full_path, config->executable) < 0) {
-        goto no_memory;
-    }
-    if (copy_wstr(&pathconfig.prefix, config->prefix) < 0) {
-        goto no_memory;
-    }
-    if (copy_wstr(&pathconfig.exec_prefix, config->exec_prefix) < 0) {
-        goto no_memory;
-    }
-    if (copy_wstr(&pathconfig.program_name, config->program_name) < 0) {
-        goto no_memory;
-    }
-    if (copy_wstr(&pathconfig.home, config->home) < 0) {
-        goto no_memory;
-    }
-    if (copy_wstr(&pathconfig.base_executable, config->base_executable) < 0) {
-        goto no_memory;
-    }
+#define COPY_CONFIG(PATH_ATTR, CONFIG_ATTR) \
+        if (config->CONFIG_ATTR) { \
+            if (copy_wstr(&pathconfig->PATH_ATTR, config->CONFIG_ATTR) < 0) { \
+                goto no_memory; \
+            } \
+        }
 
-    pathconfig_clear(&_Py_path_config);
-    /* Steal new_config strings; don't clear new_config */
-    _Py_path_config = pathconfig;
+    COPY_CONFIG(base_executable, base_executable);
+    COPY_CONFIG(program_full_path, executable);
+    COPY_CONFIG(prefix, prefix);
+    COPY_CONFIG(exec_prefix, exec_prefix);
+    COPY_CONFIG(program_name, program_name);
+    COPY_CONFIG(home, home);
+
+#undef COPY_CONFIG
 
     status = _PyStatus_OK();
     goto done;
 
 no_memory:
-    pathconfig_clear(&pathconfig);
     status = _PyStatus_NO_MEMORY();
 
 done:
@@ -283,6 +246,61 @@ config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig)
 }
 
 
+/* Calculate the path configuration:
+
+   - exec_prefix
+   - module_search_path
+   - prefix
+   - program_full_path
+
+   On Windows, more fields are calculated:
+
+   - base_executable
+   - isolated
+   - site_import
+
+   On other platforms, isolated and site_import are left unchanged, and
+   _PyConfig_InitPathConfig() copies executable to base_executable (if it's not
+   set).
+
+   Priority, highest to lowest:
+
+   - PyConfig
+   - _Py_path_config: set by Py_SetPath(), Py_SetPythonHome()
+     and Py_SetProgramName()
+   - _PyPathConfig_Calculate()
+*/
+static PyStatus
+pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
+{
+    PyStatus status;
+
+    PyMemAllocatorEx old_alloc;
+    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+
+    status = pathconfig_copy(pathconfig, &_Py_path_config);
+    if (_PyStatus_EXCEPTION(status)) {
+        goto done;
+    }
+
+    status = pathconfig_set_from_config(pathconfig, config);
+    if (_PyStatus_EXCEPTION(status)) {
+        goto done;
+    }
+
+    if (_Py_path_config.module_search_path == NULL) {
+        status = _PyPathConfig_Calculate(pathconfig, config);
+    }
+    else {
+        /* Py_SetPath() has been called: avoid _PyPathConfig_Calculate() */
+    }
+
+done:
+    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
+    return status;
+}
+
+
 static PyStatus
 config_calculate_pathconfig(PyConfig *config)
 {
@@ -291,42 +309,28 @@ config_calculate_pathconfig(PyConfig *config)
 
     status = pathconfig_calculate(&pathconfig, config);
     if (_PyStatus_EXCEPTION(status)) {
-        goto error;
+        goto done;
     }
 
     if (!config->module_search_paths_set) {
         status = config_init_module_search_paths(config, &pathconfig);
         if (_PyStatus_EXCEPTION(status)) {
-            goto error;
-        }
-    }
-
-    if (config->executable == NULL) {
-        if (copy_wstr(&config->executable,
-                      pathconfig.program_full_path) < 0) {
-            goto no_memory;
-        }
-    }
-
-    if (config->prefix == NULL) {
-        if (copy_wstr(&config->prefix, pathconfig.prefix) < 0) {
-            goto no_memory;
+            goto done;
         }
     }
 
-    if (config->exec_prefix == NULL) {
-        if (copy_wstr(&config->exec_prefix,
-                      pathconfig.exec_prefix) < 0) {
-            goto no_memory;
+#define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \
+        if (config->CONFIG_ATTR == NULL) { \
+            if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \
+                goto no_memory; \
+            } \
         }
-    }
 
-    if (config->base_executable == NULL) {
-        if (copy_wstr(&config->base_executable,
-                      pathconfig.base_executable) < 0) {
-            goto no_memory;
-        }
-    }
+    COPY_ATTR(program_full_path, executable);
+    COPY_ATTR(prefix, prefix);
+    COPY_ATTR(exec_prefix, exec_prefix);
+    COPY_ATTR(base_executable, base_executable);
+#undef COPY_ATTR
 
     if (pathconfig.isolated != -1) {
         config->isolated = pathconfig.isolated;
@@ -335,13 +339,13 @@ config_calculate_pathconfig(PyConfig *config)
         config->site_import = pathconfig.site_import;
     }
 
-    pathconfig_clear(&pathconfig);
-    return _PyStatus_OK();
+    status = _PyStatus_OK();
+    goto done;
 
 no_memory:
     status = _PyStatus_NO_MEMORY();
 
-error:
+done:
     pathconfig_clear(&pathconfig);
     return status;
 }
@@ -386,39 +390,55 @@ _PyConfig_InitPathConfig(PyConfig *config)
 }
 
 
-static void
-pathconfig_global_init(void)
+static PyStatus
+pathconfig_global_read(_PyPathConfig *pathconfig)
 {
-    /* Initialize _Py_dll_path if needed */
-    PyStatus status = _PyPathConfig_Init();
-    if (_PyStatus_EXCEPTION(status)) {
-        Py_ExitStatusException(status);
-    }
-
-    if (_Py_path_config.module_search_path != NULL) {
-        /* Already initialized */
-        return;
-    }
-
+    PyStatus status;
     PyConfig config;
     _PyConfig_InitCompatConfig(&config);
 
+    /* Call _PyConfig_InitPathConfig() */
     status = PyConfig_Read(&config);
     if (_PyStatus_EXCEPTION(status)) {
-        goto error;
+        goto done;
     }
 
-    status = pathconfig_global_init_from_config(&config);
+    status = pathconfig_set_from_config(pathconfig, &config);
+
+done:
+    PyConfig_Clear(&config);
+    return status;
+}
+
+
+static void
+pathconfig_global_init(void)
+{
+    PyStatus status;
+
+    /* Initialize _Py_dll_path if needed */
+    status = _PyPathConfig_Init();
     if (_PyStatus_EXCEPTION(status)) {
-        goto error;
+        Py_ExitStatusException(status);
     }
 
-    PyConfig_Clear(&config);
-    return;
+    if (_Py_path_config.module_search_path == NULL) {
+        status = pathconfig_global_read(&_Py_path_config);
+        if (_PyStatus_EXCEPTION(status)) {
+            Py_ExitStatusException(status);
+        }
+    }
+    else {
+        /* Global configuration already initialized */
+    }
 
-error:
-    PyConfig_Clear(&config);
-    Py_ExitStatusException(status);
+    assert(_Py_path_config.program_full_path != NULL);
+    assert(_Py_path_config.prefix != NULL);
+    assert(_Py_path_config.exec_prefix != NULL);
+    assert(_Py_path_config.module_search_path != NULL);
+    assert(_Py_path_config.program_name != NULL);
+    /* home can be NULL */
+    assert(_Py_path_config.base_executable != NULL);
 }
 
 
@@ -435,30 +455,27 @@ Py_SetPath(const wchar_t *path)
     PyMemAllocatorEx old_alloc;
     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 
-    _PyPathConfig new_config;
-    new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName());
-    int alloc_error = (new_config.program_full_path == NULL);
-    new_config.prefix = _PyMem_RawWcsdup(L"");
-    alloc_error |= (new_config.prefix == NULL);
-    new_config.exec_prefix = _PyMem_RawWcsdup(L"");
-    alloc_error |= (new_config.exec_prefix == NULL);
-    new_config.module_search_path = _PyMem_RawWcsdup(path);
-    alloc_error |= (new_config.module_search_path == NULL);
-
-    /* steal the home and program_name values (to leave them unchanged) */
-    new_config.home = _Py_path_config.home;
-    _Py_path_config.home = NULL;
-    new_config.program_name = _Py_path_config.program_name;
-    _Py_path_config.program_name = NULL;
-    new_config.base_executable = _Py_path_config.base_executable;
-    _Py_path_config.base_executable = NULL;
+    /* Getting the program name calls pathconfig_global_init() */
+    wchar_t *program_name = _PyMem_RawWcsdup(Py_GetProgramName());
 
-    pathconfig_clear(&_Py_path_config);
-    _Py_path_config = new_config;
+    PyMem_RawFree(_Py_path_config.program_full_path);
+    PyMem_RawFree(_Py_path_config.prefix);
+    PyMem_RawFree(_Py_path_config.exec_prefix);
+    PyMem_RawFree(_Py_path_config.module_search_path);
+
+    /* Copy program_name to program_full_path  */
+    _Py_path_config.program_full_path = program_name;
+    _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
+    _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
+    _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
 
     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
 
-    if (alloc_error) {
+    if (_Py_path_config.program_full_path == NULL
+        || _Py_path_config.prefix == NULL
+        || _Py_path_config.exec_prefix == NULL
+        || _Py_path_config.module_search_path == NULL)
+    {
         Py_FatalError("Py_SetPath() failed: out of memory");
     }
 }



More information about the Python-checkins mailing list