mirror of
https://github.com/python/cpython.git
synced 2025-09-29 19:56:59 +00:00
gh-86179: Implement realpath() on Windows for getpath.py calculations (GH-113033)
This commit is contained in:
parent
41c18aacc7
commit
fddc829236
4 changed files with 63 additions and 11 deletions
|
@ -404,16 +404,7 @@ def get_config_h_filename():
|
||||||
"""Return the path of pyconfig.h."""
|
"""Return the path of pyconfig.h."""
|
||||||
if _PYTHON_BUILD:
|
if _PYTHON_BUILD:
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
# This ought to be as simple as dirname(sys._base_executable), but
|
inc_dir = os.path.dirname(sys._base_executable)
|
||||||
# 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)
|
|
||||||
else:
|
else:
|
||||||
inc_dir = _PROJECT_BASE
|
inc_dir = _PROJECT_BASE
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -46,7 +46,8 @@ if is_emscripten or is_wasi:
|
||||||
def check_output(cmd, encoding=None):
|
def check_output(cmd, encoding=None):
|
||||||
p = subprocess.Popen(cmd,
|
p = subprocess.Popen(cmd,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE,
|
||||||
|
env={**os.environ, "PYTHONHOME": ""})
|
||||||
out, err = p.communicate()
|
out, err = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
if verbose and err:
|
if verbose and err:
|
||||||
|
@ -287,6 +288,16 @@ class BasicTest(BaseTest):
|
||||||
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
|
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
|
||||||
out, err = check_output(cmd, encoding='utf-8')
|
out, err = check_output(cmd, encoding='utf-8')
|
||||||
self.assertEqual(out.strip(), expected, err)
|
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
|
@requireVenvCreate
|
||||||
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
|
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
|
||||||
|
@ -309,6 +320,16 @@ class BasicTest(BaseTest):
|
||||||
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
|
cmd[2] = 'import sysconfig; print(sysconfig.%s)' % call
|
||||||
out, err = check_output(cmd, encoding='utf-8')
|
out, err = check_output(cmd, encoding='utf-8')
|
||||||
self.assertEqual(out.strip(), expected, err)
|
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':
|
if sys.platform == 'win32':
|
||||||
ENV_SUBDIRS = (
|
ENV_SUBDIRS = (
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fixes path calculations when launching Python on Windows through a symlink.
|
|
@ -502,6 +502,45 @@ done:
|
||||||
PyMem_Free((void *)path);
|
PyMem_Free((void *)path);
|
||||||
PyMem_Free((void *)narrow);
|
PyMem_Free((void *)narrow);
|
||||||
return r;
|
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
|
#endif
|
||||||
|
|
||||||
return Py_NewRef(pathobj);
|
return Py_NewRef(pathobj);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue