gh-86179: Implement realpath() on Windows for getpath.py calculations (GH-113033)

This commit is contained in:
Steve Dower 2023-12-13 23:41:43 +00:00 committed by GitHub
parent 41c18aacc7
commit fddc829236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 11 deletions

View File

@ -404,16 +404,7 @@ def get_config_h_filename():
"""Return the path of pyconfig.h."""
if _PYTHON_BUILD:
if os.name == "nt":
# This ought to be as simple as dirname(sys._base_executable), but
# if a venv uses symlinks to a build in the source tree, then this
# fails. So instead we guess the subdirectory name from sys.winver
if sys.winver.endswith('-32'):
arch = 'win32'
elif sys.winver.endswith('-arm64'):
arch = 'arm64'
else:
arch = 'amd64'
inc_dir = os.path.join(_PROJECT_BASE, 'PCbuild', arch)
inc_dir = os.path.dirname(sys._base_executable)
else:
inc_dir = _PROJECT_BASE
else:

View File

@ -46,7 +46,8 @@ if is_emscripten or is_wasi:
def check_output(cmd, encoding=None):
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr=subprocess.PIPE,
env={**os.environ, "PYTHONHOME": ""})
out, err = p.communicate()
if p.returncode:
if verbose and err:
@ -287,6 +288,16 @@ class BasicTest(BaseTest):
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
out, err = check_output(cmd, encoding='utf-8')
self.assertEqual(out.strip(), expected, err)
for attr, expected in (
('executable', self.envpy()),
# Usually compare to sys.executable, but if we're running in our own
# venv then we really need to compare to our base executable
('_base_executable', sys._base_executable),
):
with self.subTest(attr):
cmd[2] = f'import sys; print(sys.{attr})'
out, err = check_output(cmd, encoding='utf-8')
self.assertEqual(out.strip(), expected, err)
@requireVenvCreate
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
@ -309,6 +320,16 @@ class BasicTest(BaseTest):
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
out, err = check_output(cmd, encoding='utf-8')
self.assertEqual(out.strip(), expected, err)
for attr, expected in (
('executable', self.envpy()),
# Usually compare to sys.executable, but if we're running in our own
# venv then we really need to compare to our base executable
('_base_executable', sys._base_executable),
):
with self.subTest(attr):
cmd[2] = f'import sys; print(sys.{attr})'
out, err = check_output(cmd, encoding='utf-8')
self.assertEqual(out.strip(), expected, err)
if sys.platform == 'win32':
ENV_SUBDIRS = (

View File

@ -0,0 +1 @@
Fixes path calculations when launching Python on Windows through a symlink.

View File

@ -502,6 +502,45 @@ done:
PyMem_Free((void *)path);
PyMem_Free((void *)narrow);
return r;
#elif defined(MS_WINDOWS)
HANDLE hFile;
wchar_t resolved[MAXPATHLEN+1];
int len = 0, err;
PyObject *result;
wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
if (!path) {
return NULL;
}
Py_BEGIN_ALLOW_THREADS
hFile = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
len = GetFinalPathNameByHandleW(hFile, resolved, MAXPATHLEN, VOLUME_NAME_DOS);
err = len ? 0 : GetLastError();
CloseHandle(hFile);
} else {
err = GetLastError();
}
Py_END_ALLOW_THREADS
if (err) {
PyErr_SetFromWindowsErr(err);
result = NULL;
} else if (len <= MAXPATHLEN) {
const wchar_t *p = resolved;
if (0 == wcsncmp(p, L"\\\\?\\", 4)) {
if (GetFileAttributesW(&p[4]) != INVALID_FILE_ATTRIBUTES) {
p += 4;
len -= 4;
}
}
result = PyUnicode_FromWideChar(p, len);
} else {
result = Py_NewRef(pathobj);
}
PyMem_Free(path);
return result;
#endif
return Py_NewRef(pathobj);