]> granicus.if.org Git - python/commitdiff
bpo-38234: read_pth_file() now returns PyStatus (GH-16338)
authorVictor Stinner <vstinner@redhat.com>
Mon, 23 Sep 2019 22:55:48 +0000 (00:55 +0200)
committerGitHub <noreply@github.com>
Mon, 23 Sep 2019 22:55:48 +0000 (00:55 +0200)
Refactor path configuration code:

* read_pth_file() now returns PyStatus to report errors, rather than
  calling Py_FatalError().
* Move argv0_path and zip_path buffers out of PyCalculatePath
  structures.
* On Windows, _PyPathConfig.home is now preferred over PyConfig.home.

Modules/getpath.c
PC/getpathp.c

index 270355e251e18d431edf86674e519002f91234cc..24e16b41b406c98477400eb455d758b3c8e13b1c 100644 (file)
@@ -123,13 +123,11 @@ extern "C" {
 typedef struct {
     wchar_t *path_env;                 /* PATH environment variable */
 
-    wchar_t *pythonpath;               /* PYTHONPATH define */
-    wchar_t *prefix;                   /* PREFIX define */
-    wchar_t *exec_prefix;              /* EXEC_PREFIX define */
+    wchar_t *pythonpath;               /* PYTHONPATH macro */
+    wchar_t *prefix;                   /* PREFIX macro */
+    wchar_t *exec_prefix;              /* EXEC_PREFIX macro */
 
     wchar_t *lib_python;               /* "lib/pythonX.Y" */
-    wchar_t argv0_path[MAXPATHLEN+1];
-    wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
 
     int prefix_found;         /* found platform independent libraries? */
     int exec_prefix_found;    /* found the platform dependent libraries? */
@@ -369,6 +367,7 @@ add_exe_suffix(wchar_t *progpath, size_t progpathlen)
 */
 static PyStatus
 search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                  const wchar_t *argv0_path,
                   wchar_t *prefix, size_t prefix_len, int *found)
 {
     PyStatus status;
@@ -397,7 +396,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
     }
 
     /* Check to see if argv[0] is in the build directory */
-    if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) {
+    if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
         return PATHLEN_ERR();
     }
     status = joinpath(prefix, L"Modules/Setup.local", prefix_len);
@@ -409,7 +408,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
         /* Check VPATH to see if argv0_path is in the build directory. */
         vpath = Py_DecodeLocale(VPATH, NULL);
         if (vpath != NULL) {
-            if (safe_wcscpy(prefix, calculate->argv0_path, prefix_len) < 0) {
+            if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
                 return PATHLEN_ERR();
             }
             status = joinpath(prefix, vpath, prefix_len);
@@ -435,7 +434,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
     }
 
     /* Search from argv0_path, until root is found */
-    status = copy_absolute(prefix, calculate->argv0_path, prefix_len);
+    status = copy_absolute(prefix, argv0_path, prefix_len);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
@@ -485,11 +484,13 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
 
 static PyStatus
 calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                 const wchar_t *argv0_path,
                  wchar_t *prefix, size_t prefix_len)
 {
     PyStatus status;
 
-    status = search_for_prefix(calculate, pathconfig, prefix, prefix_len,
+    status = search_for_prefix(calculate, pathconfig, argv0_path,
+                               prefix, prefix_len,
                                &calculate->prefix_found);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
@@ -516,8 +517,8 @@ calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
 
 
 static PyStatus
-calculate_reduce_prefix(PyCalculatePath *calculate,
-                        wchar_t *prefix, size_t prefix_len)
+calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                     wchar_t *prefix)
 {
     /* Reduce prefix and exec_prefix to their essence,
      * e.g. /usr/local/lib/python1.5 is reduced to /usr/local.
@@ -532,11 +533,14 @@ calculate_reduce_prefix(PyCalculatePath *calculate,
         if (!prefix[0]) {
             wcscpy(prefix, separator);
         }
+        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
     }
     else {
-        if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
-            return PATHLEN_ERR();
-        }
+        pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix);
+    }
+
+    if (pathconfig->prefix == NULL) {
+        return _PyStatus_NO_MEMORY();
     }
     return _PyStatus_OK();
 }
@@ -547,6 +551,7 @@ calculate_reduce_prefix(PyCalculatePath *calculate,
 */
 static PyStatus
 search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                       const wchar_t *argv0_path,
                        wchar_t *exec_prefix, size_t exec_prefix_len,
                        int *found)
 {
@@ -581,7 +586,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
     /* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
        is written by setup.py and contains the relative path to the location
        of shared library modules. */
-    if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) {
+    if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
         return PATHLEN_ERR();
     }
     status = joinpath(exec_prefix, L"pybuilddir.txt", exec_prefix_len);
@@ -607,7 +612,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
                 return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
             }
 
-            if (safe_wcscpy(exec_prefix, calculate->argv0_path, exec_prefix_len) < 0) {
+            if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
                 return PATHLEN_ERR();
             }
             status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
@@ -622,7 +627,7 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
     }
 
     /* Search from argv0_path, until root is found */
-    status = copy_absolute(exec_prefix, calculate->argv0_path, exec_prefix_len);
+    status = copy_absolute(exec_prefix, argv0_path, exec_prefix_len);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
@@ -670,11 +675,12 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
 
 static PyStatus
 calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+                      const wchar_t *argv0_path,
                       wchar_t *exec_prefix, size_t exec_prefix_len)
 {
     PyStatus status;
 
-    status = search_for_exec_prefix(calculate, pathconfig,
+    status = search_for_exec_prefix(calculate, pathconfig, argv0_path,
                                     exec_prefix, exec_prefix_len,
                                     &calculate->exec_prefix_found);
     if (_PyStatus_EXCEPTION(status)) {
@@ -700,8 +706,9 @@ calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
 
 
 static PyStatus
-calculate_reduce_exec_prefix(PyCalculatePath *calculate,
-                             wchar_t *exec_prefix, size_t exec_prefix_len)
+calculate_set_exec_prefix(PyCalculatePath *calculate,
+                          _PyPathConfig *pathconfig,
+                          wchar_t *exec_prefix)
 {
     if (calculate->exec_prefix_found > 0) {
         reduce(exec_prefix);
@@ -710,12 +717,17 @@ calculate_reduce_exec_prefix(PyCalculatePath *calculate,
         if (!exec_prefix[0]) {
             wcscpy(exec_prefix, separator);
         }
+
+        pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
     }
     else {
-        if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
-            return PATHLEN_ERR();
-        }
+        pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix);
+    }
+
+    if (pathconfig->exec_prefix == NULL) {
+        return _PyStatus_NO_MEMORY();
     }
+
     return _PyStatus_OK();
 }
 
@@ -843,10 +855,10 @@ calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfi
 
 
 static PyStatus
-calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path)
+calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path,
+                     wchar_t *argv0_path, size_t argv0_path_len)
 {
-    const size_t argv0_path_len = Py_ARRAY_LENGTH(calculate->argv0_path);
-    if (safe_wcscpy(calculate->argv0_path, program_full_path, argv0_path_len) < 0) {
+    if (safe_wcscpy(argv0_path, program_full_path, argv0_path_len) < 0) {
         return PATHLEN_ERR();
     }
 
@@ -877,32 +889,31 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
             return DECODE_LOCALE_ERR("framework location", len);
         }
 
-        if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) {
+        if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
             return PATHLEN_ERR();
         }
-        reduce(calculate->argv0_path);
-        status = joinpath(calculate->argv0_path, calculate->lib_python, argv0_path_len);
+        reduce(argv0_path);
+        status = joinpath(argv0_path, calculate->lib_python, argv0_path_len);
         if (_PyStatus_EXCEPTION(status)) {
             PyMem_RawFree(wbuf);
             return status;
         }
-        status = joinpath(calculate->argv0_path, LANDMARK, argv0_path_len);
+        status = joinpath(argv0_path, LANDMARK, argv0_path_len);
         if (_PyStatus_EXCEPTION(status)) {
             PyMem_RawFree(wbuf);
             return status;
         }
-        if (!ismodule(calculate->argv0_path,
-                      Py_ARRAY_LENGTH(calculate->argv0_path))) {
+        if (!ismodule(argv0_path, Py_ARRAY_LENGTH(argv0_path))) {
             /* We are in the build directory so use the name of the
                executable - we know that the absolute path is passed */
-            if (safe_wcscpy(calculate->argv0_path, program_full_path,
+            if (safe_wcscpy(argv0_path, program_full_path,
                             argv0_path_len) < 0) {
                 return PATHLEN_ERR();
             }
         }
         else {
             /* Use the location of the library as the program_full_path */
-            if (safe_wcscpy(calculate->argv0_path, wbuf, argv0_path_len) < 0) {
+            if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
                 return PATHLEN_ERR();
             }
         }
@@ -918,24 +929,24 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
         if (_Py_isabs(tmpbuffer)) {
             /* tmpbuffer should never be longer than MAXPATHLEN,
                but extra check does not hurt */
-            if (safe_wcscpy(calculate->argv0_path, tmpbuffer, argv0_path_len) < 0) {
+            if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
                 return PATHLEN_ERR();
             }
         }
         else {
             /* Interpret relative to program_full_path */
             PyStatus status;
-            reduce(calculate->argv0_path);
-            status = joinpath(calculate->argv0_path, tmpbuffer, argv0_path_len);
+            reduce(argv0_path);
+            status = joinpath(argv0_path, tmpbuffer, argv0_path_len);
             if (_PyStatus_EXCEPTION(status)) {
                 return status;
             }
         }
-        linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, buflen);
+        linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen);
     }
 #endif /* HAVE_READLINK */
 
-    reduce(calculate->argv0_path);
+    reduce(argv0_path);
     /* At this point, argv0_path is guaranteed to be less than
        MAXPATHLEN bytes long. */
     return _PyStatus_OK();
@@ -947,7 +958,8 @@ calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_pat
    If found, open it for use when searching for prefixes.
 */
 static PyStatus
-calculate_read_pyenv(PyCalculatePath *calculate)
+calculate_read_pyenv(PyCalculatePath *calculate,
+                     wchar_t *argv0_path, size_t argv0_path_len)
 {
     PyStatus status;
     wchar_t tmpbuffer[MAXPATHLEN+1];
@@ -955,7 +967,7 @@ calculate_read_pyenv(PyCalculatePath *calculate)
     wchar_t *env_cfg = L"pyvenv.cfg";
     FILE *env_file;
 
-    if (safe_wcscpy(tmpbuffer, calculate->argv0_path, buflen) < 0) {
+    if (safe_wcscpy(tmpbuffer, argv0_path, buflen) < 0) {
         return PATHLEN_ERR();
     }
 
@@ -986,8 +998,7 @@ calculate_read_pyenv(PyCalculatePath *calculate)
 
     /* Look for a 'home' variable and set argv0_path to it, if found */
     if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, buflen)) {
-        if (safe_wcscpy(calculate->argv0_path, tmpbuffer,
-                        Py_ARRAY_LENGTH(calculate->argv0_path)) < 0) {
+        if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
             return PATHLEN_ERR();
         }
     }
@@ -997,33 +1008,33 @@ calculate_read_pyenv(PyCalculatePath *calculate)
 
 
 static PyStatus
-calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
+calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix,
+                   wchar_t *zip_path, size_t zip_path_len)
 {
     PyStatus status;
-    const size_t zip_path_len = Py_ARRAY_LENGTH(calculate->zip_path);
-    if (safe_wcscpy(calculate->zip_path, prefix, zip_path_len) < 0) {
+    if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) {
         return PATHLEN_ERR();
     }
 
     if (calculate->prefix_found > 0) {
         /* Use the reduced prefix returned by Py_GetPrefix() */
-        reduce(calculate->zip_path);
-        reduce(calculate->zip_path);
+        reduce(zip_path);
+        reduce(zip_path);
     }
     else {
-        if (safe_wcscpy(calculate->zip_path, calculate->prefix, zip_path_len) < 0) {
+        if (safe_wcscpy(zip_path, calculate->prefix, zip_path_len) < 0) {
             return PATHLEN_ERR();
         }
     }
-    status = joinpath(calculate->zip_path, L"lib/python00.zip", zip_path_len);
+    status = joinpath(zip_path, L"lib/python00.zip", zip_path_len);
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
     /* Replace "00" with version */
-    size_t bufsz = wcslen(calculate->zip_path);
-    calculate->zip_path[bufsz - 6] = VERSION[0];
-    calculate->zip_path[bufsz - 5] = VERSION[2];
+    size_t bufsz = wcslen(zip_path);
+    zip_path[bufsz - 6] = VERSION[0];
+    zip_path[bufsz - 5] = VERSION[2];
     return _PyStatus_OK();
 }
 
@@ -1031,7 +1042,9 @@ calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
 static PyStatus
 calculate_module_search_path(PyCalculatePath *calculate,
                              _PyPathConfig *pathconfig,
-                             const wchar_t *prefix, const wchar_t *exec_prefix)
+                             const wchar_t *prefix,
+                             const wchar_t *exec_prefix,
+                             const wchar_t *zip_path)
 {
     /* Calculate size of return buffer */
     size_t bufsz = 0;
@@ -1059,7 +1072,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
         defpath = delim + 1;
     }
 
-    bufsz += wcslen(calculate->zip_path) + 1;
+    bufsz += wcslen(zip_path) + 1;
     bufsz += wcslen(exec_prefix) + 1;
 
     /* Allocate the buffer */
@@ -1076,7 +1089,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
     }
 
     /* Next is the default zip path */
-    wcscat(buf, calculate->zip_path);
+    wcscat(buf, zip_path);
     wcscat(buf, delimiter);
 
     /* Next goes merge of compile-time $PYTHONPATH with
@@ -1119,8 +1132,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
 
 
 static PyStatus
-calculate_init(PyCalculatePath *calculate,
-               const PyConfig *config)
+calculate_init(PyCalculatePath *calculate, const PyConfig *config)
 {
     size_t len;
     const char *path = getenv("PATH");
@@ -1135,6 +1147,7 @@ calculate_init(PyCalculatePath *calculate,
     if (!calculate->pythonpath) {
         return DECODE_LOCALE_ERR("PYTHONPATH define", len);
     }
+
     calculate->prefix = Py_DecodeLocale(PREFIX, &len);
     if (!calculate->prefix) {
         return DECODE_LOCALE_ERR("PREFIX define", len);
@@ -1178,12 +1191,17 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
         }
     }
 
-    status = calculate_argv0_path(calculate, pathconfig->program_full_path);
+    wchar_t argv0_path[MAXPATHLEN+1];
+    memset(argv0_path, 0, sizeof(argv0_path));
+
+    status = calculate_argv0_path(calculate, pathconfig->program_full_path,
+                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
-    status = calculate_read_pyenv(calculate);
+    status = calculate_read_pyenv(calculate,
+                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
@@ -1191,19 +1209,24 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
     wchar_t prefix[MAXPATHLEN+1];
     memset(prefix, 0, sizeof(prefix));
     status = calculate_prefix(calculate, pathconfig,
+                              argv0_path,
                               prefix, Py_ARRAY_LENGTH(prefix));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
-    status = calculate_zip_path(calculate, prefix);
+    wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
+    memset(zip_path, 0, sizeof(zip_path));
+
+    status = calculate_zip_path(calculate, prefix,
+                                zip_path, Py_ARRAY_LENGTH(zip_path));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
     }
 
     wchar_t exec_prefix[MAXPATHLEN+1];
     memset(exec_prefix, 0, sizeof(exec_prefix));
-    status = calculate_exec_prefix(calculate, pathconfig,
+    status = calculate_exec_prefix(calculate, pathconfig, argv0_path,
                                    exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
     if (_PyStatus_EXCEPTION(status)) {
         return status;
@@ -1218,50 +1241,60 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
 
     if (pathconfig->module_search_path == NULL) {
         status = calculate_module_search_path(calculate, pathconfig,
-                                              prefix, exec_prefix);
+                                              prefix, exec_prefix, zip_path);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
     }
 
     if (pathconfig->prefix == NULL) {
-        status = calculate_reduce_prefix(calculate, prefix, Py_ARRAY_LENGTH(prefix));
+        status = calculate_set_prefix(calculate, pathconfig, prefix);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
-
-        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
-        if (pathconfig->prefix == NULL) {
-            return _PyStatus_NO_MEMORY();
-        }
     }
 
     if (pathconfig->exec_prefix == NULL) {
-        status = calculate_reduce_exec_prefix(calculate,
-                                              exec_prefix,
-                                              Py_ARRAY_LENGTH(exec_prefix));
+        status = calculate_set_exec_prefix(calculate, pathconfig, 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:
+/* Calculate the Python path configuration.
+
+   Inputs:
+
+   - PATH environment variable
+   - Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9").
+     PREFIX and EXEC_PREFIX are generated by the configure script.
+     PYTHONPATH macro is the default search path.
+   - pybuilddir.txt file
+   - pyvenv.cfg configuration file
+   - PyConfig fields ('config' function argument):
+
+     - pathconfig_warnings
+     - pythonpath_env (PYTHONPATH environment variable)
+
+   - _PyPathConfig fields ('pathconfig' function argument):
+
+     - program_name: see config_init_program_name()
+     - home: Py_SetPythonHome() or PYTHONHOME environment variable
+
+   - current working directory: see copy_absolute()
+
+   Outputs, 'pathconfig' fields:
 
    - program_full_path
    - module_search_path
    - prefix
    - exec_prefix
 
-   If an attribute is already set (non NULL), it is left unchanged. */
+   If a field is already set (non NULL), it is left unchanged. */
 PyStatus
 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
 {
index 5670fa2ee4e18e58dcc74d085b283f6776101791..c4c0636dddeb1fc2a9ac8bcf8c67d7cf8494e90e 100644 (file)
  */
 
 #ifndef LANDMARK
-#define LANDMARK L"lib\\os.py"
+#  define LANDMARK L"lib\\os.py"
 #endif
 
+#define INIT_ERR_BUFFER_OVERFLOW() _PyStatus_ERR("buffer overflow")
+
+
 typedef struct {
     const wchar_t *path_env;           /* PATH environment variable */
     const wchar_t *home;               /* PYTHONHOME environment variable */
 
-    /* Registry key "Software\Python\PythonCore\PythonPath" */
+    /* Registry key "Software\Python\PythonCore\X.Y\PythonPath"
+       where X.Y is the Python version (major.minor) */
     wchar_t *machine_path;   /* from HKEY_LOCAL_MACHINE */
     wchar_t *user_path;      /* from HKEY_CURRENT_USER */
 
-    wchar_t argv0_path[MAXPATHLEN+1];
-    wchar_t zip_path[MAXPATHLEN+1];
-
     wchar_t *dll_path;
 
     const wchar_t *pythonpath_env;
@@ -276,7 +277,10 @@ typedef HRESULT(__stdcall *PPathCchCanonicalizeEx) (PWSTR pszPathOut, size_t cch
     PCWSTR pszPathIn, unsigned long dwFlags);
 static PPathCchCanonicalizeEx _PathCchCanonicalizeEx;
 
-static PyStatus canonicalize(wchar_t *buffer, const wchar_t *path)
+/* Call PathCchCanonicalizeEx(path): remove navigation elements such as "."
+   and ".." to produce a direct, well-formed path. */
+static PyStatus
+canonicalize(wchar_t *buffer, const wchar_t *path)
 {
     if (buffer == NULL) {
         return _PyStatus_NO_MEMORY();
@@ -295,12 +299,12 @@ static PyStatus canonicalize(wchar_t *buffer, const wchar_t *path)
 
     if (_PathCchCanonicalizeEx) {
         if (FAILED(_PathCchCanonicalizeEx(buffer, MAXPATHLEN + 1, path, 0))) {
-            return _PyStatus_ERR("buffer overflow in getpathp.c's canonicalize()");
+            return INIT_ERR_BUFFER_OVERFLOW();
         }
     }
     else {
         if (!PathCanonicalizeW(buffer, path)) {
-            return _PyStatus_ERR("buffer overflow in getpathp.c's canonicalize()");
+            return INIT_ERR_BUFFER_OVERFLOW();
         }
     }
     return _PyStatus_OK();
@@ -588,12 +592,18 @@ get_program_full_path(_PyPathConfig *pathconfig)
 }
 
 
-static int
-read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
+static PyStatus
+read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path,
+              int *found)
 {
-    FILE *sp_file = _Py_wfopen(path, L"r");
+    PyStatus status;
+    wchar_t *buf = NULL;
+    wchar_t *wline = NULL;
+    FILE *sp_file;
+
+    sp_file = _Py_wfopen(path, L"r");
     if (sp_file == NULL) {
-        return 0;
+        return _PyStatus_OK();
     }
 
     wcscpy_s(prefix, MAXPATHLEN+1, path);
@@ -604,15 +614,16 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
     size_t bufsiz = MAXPATHLEN;
     size_t prefixlen = wcslen(prefix);
 
-    wchar_t *buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t));
+    buf = (wchar_t*)PyMem_RawMalloc(bufsiz * sizeof(wchar_t));
     if (buf == NULL) {
-        goto error;
+        status = _PyStatus_NO_MEMORY();
+        goto done;
     }
     buf[0] = '\0';
 
     while (!feof(sp_file)) {
         char line[MAXPATHLEN + 1];
-        char *p = fgets(line, MAXPATHLEN + 1, sp_file);
+        char *p = fgets(line, Py_ARRAY_LENGTH(line), sp_file);
         if (!p) {
             break;
         }
@@ -631,13 +642,16 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
             continue;
         }
         else if (strncmp(line, "import ", 7) == 0) {
-            Py_FatalError("only 'import site' is supported in ._pth file");
+            status = _PyStatus_ERR("only 'import site' is supported "
+                                   "in ._pth file");
+            goto done;
         }
 
         DWORD wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, NULL, 0);
         wchar_t *wline = (wchar_t*)PyMem_RawMalloc((wn + 1) * sizeof(wchar_t));
         if (wline == NULL) {
-            goto error;
+            status = _PyStatus_NO_MEMORY();
+            goto done;
         }
         wn = MultiByteToWideChar(CP_UTF8, 0, line, -1, wline, wn + 1);
         wline[wn] = '\0';
@@ -648,8 +662,8 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
             wchar_t *tmp = (wchar_t*)PyMem_RawRealloc(buf, (bufsiz + 1) *
                                                             sizeof(wchar_t));
             if (tmp == NULL) {
-                PyMem_RawFree(wline);
-                goto error;
+                status = _PyStatus_NO_MEMORY();
+                goto done;
             }
             buf = tmp;
         }
@@ -663,48 +677,39 @@ read_pth_file(_PyPathConfig *pathconfig, wchar_t *prefix, const wchar_t *path)
         _Py_BEGIN_SUPPRESS_IPH
         result = wcscat_s(buf, bufsiz, prefix);
         _Py_END_SUPPRESS_IPH
+
         if (result == EINVAL) {
-            Py_FatalError("invalid argument during ._pth processing");
+            status = _PyStatus_ERR("invalid argument during ._pth processing");
+            goto done;
         } else if (result == ERANGE) {
-            Py_FatalError("buffer overflow during ._pth processing");
+            status = _PyStatus_ERR("buffer overflow during ._pth processing");
+            goto done;
         }
+
         wchar_t *b = &buf[usedsiz];
         join(b, wline);
 
         PyMem_RawFree(wline);
+        wline = NULL;
     }
 
-    fclose(sp_file);
     if (pathconfig->module_search_path == NULL) {
         pathconfig->module_search_path = _PyMem_RawWcsdup(buf);
         if (pathconfig->module_search_path == NULL) {
-            Py_FatalError("out of memory");
+            status = _PyStatus_NO_MEMORY();
+            goto done;
         }
     }
-    PyMem_RawFree(buf);
-    return 1;
 
-error:
+    *found = 1;
+    status = _PyStatus_OK();
+    goto done;
+
+done:
     PyMem_RawFree(buf);
+    PyMem_RawFree(wline);
     fclose(sp_file);
-    return 0;
-}
-
-
-static PyStatus
-calculate_init(PyCalculatePath *calculate, const PyConfig *config)
-{
-    calculate->home = config->home;
-    calculate->path_env = _wgetenv(L"PATH");
-
-    calculate->dll_path = _Py_GetDLLPath();
-    if (calculate->dll_path == NULL) {
-        return _PyStatus_NO_MEMORY();
-    }
-
-    calculate->pythonpath_env = config->pythonpath_env;
-
-    return _PyStatus_OK();
+    return status;
 }
 
 
@@ -730,17 +735,17 @@ get_pth_filename(PyCalculatePath *calculate, wchar_t *filename,
 }
 
 
-static int
+static PyStatus
 calculate_pth_file(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
-                   wchar_t *prefix)
+                   wchar_t *prefix, int *found)
 {
     wchar_t filename[MAXPATHLEN+1];
 
     if (!get_pth_filename(calculate, filename, pathconfig)) {
-        return 0;
+        return _PyStatus_OK();
     }
 
-    return read_pth_file(pathconfig, prefix, filename);
+    return read_pth_file(pathconfig, prefix, filename, found);
 }
 
 
@@ -749,12 +754,13 @@ calculate_pth_file(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
    If found, open it for use when searching for prefixes.
 */
 static void
-calculate_pyvenv_file(PyCalculatePath *calculate)
+calculate_pyvenv_file(PyCalculatePath *calculate,
+                      wchar_t *argv0_path, size_t argv0_path_len)
 {
     wchar_t envbuffer[MAXPATHLEN+1];
     const wchar_t *env_cfg = L"pyvenv.cfg";
 
-    wcscpy_s(envbuffer, MAXPATHLEN+1, calculate->argv0_path);
+    wcscpy_s(envbuffer, MAXPATHLEN+1, argv0_path);
     join(envbuffer, env_cfg);
 
     FILE *env_file = _Py_wfopen(envbuffer, L"r");
@@ -778,25 +784,25 @@ calculate_pyvenv_file(PyCalculatePath *calculate)
     /* Look for a 'home' variable and set argv0_path to it, if found */
     wchar_t tmpbuffer[MAXPATHLEN+1];
     if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) {
-        wcscpy_s(calculate->argv0_path, MAXPATHLEN+1, tmpbuffer);
+        wcscpy_s(argv0_path, argv0_path_len, tmpbuffer);
     }
     fclose(env_file);
 }
 
 
-#define INIT_ERR_BUFFER_OVERFLOW() _PyStatus_ERR("buffer overflow")
-
-
 static void
-calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix)
+calculate_home_prefix(PyCalculatePath *calculate,
+                      const wchar_t *argv0_path,
+                      const wchar_t *zip_path,
+                      wchar_t *prefix)
 {
     if (calculate->home == NULL || *calculate->home == '\0') {
-        if (calculate->zip_path[0] && exists(calculate->zip_path)) {
-            wcscpy_s(prefix, MAXPATHLEN+1, calculate->zip_path);
+        if (zip_path[0] && exists(zip_path)) {
+            wcscpy_s(prefix, MAXPATHLEN+1, zip_path);
             reduce(prefix);
             calculate->home = prefix;
         }
-        else if (search_for_prefix(prefix, calculate->argv0_path, LANDMARK)) {
+        else if (search_for_prefix(prefix, argv0_path, LANDMARK)) {
             calculate->home = prefix;
         }
         else {
@@ -812,7 +818,9 @@ calculate_home_prefix(PyCalculatePath *calculate, wchar_t *prefix)
 static PyStatus
 calculate_module_search_path(PyCalculatePath *calculate,
                              _PyPathConfig *pathconfig,
-                             wchar_t *prefix)
+                             const wchar_t *argv0_path,
+                             wchar_t *prefix,
+                             const wchar_t *zip_path)
 {
     int skiphome = calculate->home==NULL ? 0 : 1;
 #ifdef Py_ENABLE_SHARED
@@ -852,14 +860,14 @@ calculate_module_search_path(PyCalculatePath *calculate,
         bufsz *= wcslen(calculate->home);
     }
     bufsz += wcslen(PYTHONPATH) + 1;
-    bufsz += wcslen(calculate->argv0_path) + 1;
+    bufsz += wcslen(argv0_path) + 1;
     if (calculate->user_path) {
         bufsz += wcslen(calculate->user_path) + 1;
     }
     if (calculate->machine_path) {
         bufsz += wcslen(calculate->machine_path) + 1;
     }
-    bufsz += wcslen(calculate->zip_path) + 1;
+    bufsz += wcslen(zip_path) + 1;
     if (calculate->pythonpath_env != NULL) {
         bufsz += wcslen(calculate->pythonpath_env) + 1;
     }
@@ -867,7 +875,7 @@ calculate_module_search_path(PyCalculatePath *calculate,
     wchar_t *buf, *start_buf;
     buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
     if (buf == NULL) {
-        Py_FatalError("Can't malloc dynamic PYTHONPATH");
+        return _PyStatus_NO_MEMORY();
     }
     start_buf = buf;
 
@@ -879,8 +887,8 @@ calculate_module_search_path(PyCalculatePath *calculate,
         buf = wcschr(buf, L'\0');
         *buf++ = DELIM;
     }
-    if (calculate->zip_path[0]) {
-        if (wcscpy_s(buf, bufsz - (buf - start_buf), calculate->zip_path)) {
+    if (zip_path[0]) {
+        if (wcscpy_s(buf, bufsz - (buf - start_buf), zip_path)) {
             return INIT_ERR_BUFFER_OVERFLOW();
         }
         buf = wcschr(buf, L'\0');
@@ -937,8 +945,8 @@ calculate_module_search_path(PyCalculatePath *calculate,
             p = q+1;
         }
     }
-    if (calculate->argv0_path) {
-        wcscpy(buf, calculate->argv0_path);
+    if (argv0_path) {
+        wcscpy(buf, argv0_path);
         buf = wcschr(buf, L'\0');
         *buf++ = DELIM;
     }
@@ -996,28 +1004,40 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
     }
 
     /* program_full_path guaranteed \0 terminated in MAXPATH+1 bytes. */
-    wcscpy_s(calculate->argv0_path, MAXPATHLEN+1, pathconfig->program_full_path);
-    reduce(calculate->argv0_path);
+    wchar_t argv0_path[MAXPATHLEN+1];
+    memset(argv0_path, 0, sizeof(argv0_path));
+
+    wcscpy_s(argv0_path, MAXPATHLEN+1, pathconfig->program_full_path);
+    reduce(argv0_path);
 
     wchar_t prefix[MAXPATHLEN+1];
     memset(prefix, 0, sizeof(prefix));
 
     /* Search for a sys.path file */
-    if (calculate_pth_file(calculate, pathconfig, prefix)) {
+    int pth_found = 0;
+    status = calculate_pth_file(calculate, pathconfig, prefix, &pth_found);
+    if (_PyStatus_EXCEPTION(status)) {
+        return status;
+    }
+    if (pth_found) {
         goto done;
     }
 
-    calculate_pyvenv_file(calculate);
+    calculate_pyvenv_file(calculate, argv0_path, Py_ARRAY_LENGTH(argv0_path));
 
     /* Calculate zip archive path from DLL or exe path */
-    change_ext(calculate->zip_path,
+    wchar_t zip_path[MAXPATHLEN+1];
+    memset(zip_path, 0, sizeof(zip_path));
+
+    change_ext(zip_path,
                calculate->dll_path[0] ? calculate->dll_path : pathconfig->program_full_path,
                L".zip");
 
-    calculate_home_prefix(calculate, prefix);
+    calculate_home_prefix(calculate, argv0_path, zip_path, prefix);
 
     if (pathconfig->module_search_path == NULL) {
-        status = calculate_module_search_path(calculate, pathconfig, prefix);
+        status = calculate_module_search_path(calculate, pathconfig,
+                                              argv0_path, prefix, zip_path);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
@@ -1041,6 +1061,24 @@ done:
 }
 
 
+static PyStatus
+calculate_init(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+               const PyConfig *config)
+{
+    calculate->home = pathconfig->home;
+    calculate->path_env = _wgetenv(L"PATH");
+
+    calculate->dll_path = _Py_GetDLLPath();
+    if (calculate->dll_path == NULL) {
+        return _PyStatus_NO_MEMORY();
+    }
+
+    calculate->pythonpath_env = config->pythonpath_env;
+
+    return _PyStatus_OK();
+}
+
+
 static void
 calculate_free(PyCalculatePath *calculate)
 {
@@ -1050,7 +1088,24 @@ calculate_free(PyCalculatePath *calculate)
 }
 
 
-/* Calculate 'pathconfig' attributes:
+/* Calculate the Python path configuration.
+
+   Inputs:
+
+   - PyConfig.pythonpath_env: PYTHONPATH environment variable
+   - _PyPathConfig.home: Py_SetPythonHome() or PYTHONHOME environment variable
+   - DLL path: _Py_GetDLLPath()
+   - PATH environment variable
+   - __PYVENV_LAUNCHER__ environment variable
+   - GetModuleFileNameW(NULL): fully qualified path of the executable file of
+     the current process
+   - .pth configuration file
+   - pyvenv.cfg configuration file
+   - Registry key "Software\Python\PythonCore\X.Y\PythonPath"
+     of HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER where X.Y is the Python
+     version (major.minor).
+
+   Outputs, 'pathconfig' fields:
 
    - base_executable
    - program_full_path
@@ -1060,7 +1115,7 @@ calculate_free(PyCalculatePath *calculate)
    - isolated
    - site_import
 
-   If an attribute is already set (non NULL), it is left unchanged. */
+   If a field is already set (non NULL), it is left unchanged. */
 PyStatus
 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
 {
@@ -1068,7 +1123,7 @@ _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
     PyCalculatePath calculate;
     memset(&calculate, 0, sizeof(calculate));
 
-    status = calculate_init(&calculate, config);
+    status = calculate_init(&calculate, pathconfig, config);
     if (_PyStatus_EXCEPTION(status)) {
         goto done;
     }