From f8523063e6d99d4d8a623825e86e3d62e47a9271 Mon Sep 17 00:00:00 2001
From: Christoph Reiter <reiter.christoph@gmail.com>
Date: Fri, 28 Jul 2023 20:13:58 +0200
Subject: [PATCH 096/N] getpath: use normpath on all generated paths

Instead of just calling normpath in abspath just call normpath
on all the config results. This makes sure we don't change getpath.py
too much and still cover all outputs.

This fixes sys.exec_prefix not matching sys.prefix, see
https://github.com/msys2-contrib/cpython-mingw/issues/142
---
 Lib/test/test_getpath.py |  6 ++++++
 Modules/getpath.c        | 21 ++++++++++++++++++++-
 Modules/getpath.py       | 30 ++++++++++++++++--------------
 3 files changed, 42 insertions(+), 15 deletions(-)

diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py
index 4dc2d30..f63e420 100644
--- a/Lib/test/test_getpath.py
+++ b/Lib/test/test_getpath.py
@@ -913,6 +913,9 @@ class MockNTNamespace(dict):
         except AttributeError:
             raise KeyError(key) from None
 
+    def normpath(self, path):
+        return ntpath.normpath(path)
+
     def abspath(self, path):
         if self.isabs(path):
             return path
@@ -1091,6 +1094,9 @@ class MockPosixNamespace(dict):
         except AttributeError:
             raise KeyError(key) from None
 
+    def normpath(self, path):
+        return path
+
     def abspath(self, path):
         if self.isabs(path):
             return path
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 099cffc..47b89eb 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -54,6 +54,25 @@
 
 /* HELPER FUNCTIONS for getpath.py */
 
+static PyObject *
+getpath_normpath(PyObject *Py_UNUSED(self), PyObject *args)
+{
+    PyObject *r = NULL;
+    PyObject *pathobj;
+    wchar_t *path;
+    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
+        return NULL;
+    }
+    Py_ssize_t len;
+    wchar_t *buffer = PyUnicode_AsWideCharString(pathobj, &len);
+    if (!buffer) {
+        return NULL;
+    }
+    r = PyUnicode_FromWideChar(_Py_normpath(buffer, len), -1);
+    PyMem_Free(buffer);
+    return r;
+}
+
 static PyObject *
 getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args)
 {
@@ -68,7 +87,6 @@ getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args)
     if (path) {
         wchar_t *abs;
         if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) {
-            abs = _Py_normpath(abs, -1);
             r = PyUnicode_FromWideChar(abs, -1);
             PyMem_RawFree((void *)abs);
         } else {
@@ -523,6 +541,7 @@ done:
 
 
 static PyMethodDef getpath_methods[] = {
+    {"normpath", getpath_normpath, METH_VARARGS, NULL},
     {"abspath", getpath_abspath, METH_VARARGS, NULL},
     {"basename", getpath_basename, METH_VARARGS, NULL},
     {"dirname", getpath_dirname, METH_VARARGS, NULL},
diff --git a/Modules/getpath.py b/Modules/getpath.py
index 5687e4f..d143f97 100644
--- a/Modules/getpath.py
+++ b/Modules/getpath.py
@@ -233,6 +233,8 @@ def search_up(prefix, *landmarks, test=isfile):
             return prefix
         prefix = dirname(prefix)
 
+def _normpath(p):
+    return normpath(p) if p is not None else None
 
 # ******************************************************************************
 # READ VARIABLES FROM config
@@ -670,7 +672,7 @@ else:
 
 if py_setpath:
     # If Py_SetPath was called then it overrides any existing search path
-    config['module_search_paths'] = py_setpath.split(DELIM)
+    config['module_search_paths'] = [_normpath(p) for p in py_setpath.split(DELIM)]
     config['module_search_paths_set'] = 1
 
 elif not pythonpath_was_set:
@@ -757,7 +759,7 @@ elif not pythonpath_was_set:
         if platstdlib_dir:
             pythonpath.append(platstdlib_dir)
 
-    config['module_search_paths'] = pythonpath
+    config['module_search_paths'] = [_normpath(p) for p in pythonpath]
     config['module_search_paths_set'] = 1
 
 
@@ -792,23 +794,23 @@ if pth:
             warn("unsupported 'import' line in ._pth file")
         else:
             pythonpath.append(joinpath(pth_dir, line))
-    config['module_search_paths'] = pythonpath
+    config['module_search_paths'] = [_normpath(p) for p in pythonpath]
     config['module_search_paths_set'] = 1
 
 # ******************************************************************************
 # UPDATE config FROM CALCULATED VALUES
 # ******************************************************************************
 
-config['program_name'] = program_name
-config['home'] = home
-config['executable'] = executable
-config['base_executable'] = base_executable
-config['prefix'] = prefix
-config['exec_prefix'] = exec_prefix
-config['base_prefix'] = base_prefix or prefix
-config['base_exec_prefix'] = base_exec_prefix or exec_prefix
+config['program_name'] = _normpath(program_name)
+config['home'] = _normpath(home)
+config['executable'] = _normpath(executable)
+config['base_executable'] = _normpath(base_executable)
+config['prefix'] = _normpath(prefix)
+config['exec_prefix'] = _normpath(exec_prefix)
+config['base_prefix'] = _normpath(base_prefix or prefix)
+config['base_exec_prefix'] = _normpath(base_exec_prefix or exec_prefix)
 
-config['platlibdir'] = platlibdir
+config['platlibdir'] = _normpath(platlibdir)
 # test_embed expects empty strings, not None
-config['stdlib_dir'] = stdlib_dir or ''
-config['platstdlib_dir'] = platstdlib_dir or ''
+config['stdlib_dir'] = _normpath(stdlib_dir or '')
+config['platstdlib_dir'] = _normpath(platstdlib_dir or '')
