https://github.com/python/cpython/commit/0d71cc460f5ac82566d63664dcc74d0ec503c7c2
commit: 0d71cc460f5ac82566d63664dcc74d0ec503c7c2
branch: main
author: thexai <[email protected]>
committer: zooba <[email protected]>
date: 2026-07-24T10:56:48+01:00
summary:
gh-152433: Windows: modernize and refactor stat emulation APIs (GH-153700)
This allows build on more API sets (e.g. UWP) without excluding any currently
supported desktop OS.
files:
A Misc/NEWS.d/next/Windows/2026-07-17-16-21-17.gh-issue-152433.45rR2A.rst
M Modules/posixmodule.c
M Python/fileutils.c
diff --git
a/Misc/NEWS.d/next/Windows/2026-07-17-16-21-17.gh-issue-152433.45rR2A.rst
b/Misc/NEWS.d/next/Windows/2026-07-17-16-21-17.gh-issue-152433.45rR2A.rst
new file mode 100644
index 00000000000000..eb45c1cbd0e9ea
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2026-07-17-16-21-17.gh-issue-152433.45rR2A.rst
@@ -0,0 +1,2 @@
+Modernize fileutils removing GetFileInformationByHandle API calls and allow
+build for Universal Windows Platform.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index de957578188156..350f56c393d3b9 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -829,9 +829,9 @@ PyOS_AfterFork(void)
#ifdef MS_WINDOWS
/* defined in fileutils.c */
void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
-void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG,
- FILE_BASIC_INFO *, FILE_ID_INFO *,
- struct _Py_stat_struct *);
+void _Py_attribute_data_to_stat(FILE_STANDARD_INFO*, ULONG,
+ FILE_BASIC_INFO*, FILE_ID_INFO*,
+ struct _Py_stat_struct*);
void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *,
struct _Py_stat_struct *);
#endif
@@ -2025,17 +2025,23 @@ win32_wchdir(LPCWSTR path)
static void
find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
- BY_HANDLE_FILE_INFORMATION *info,
+ FILE_BASIC_INFO* basic_info,
+ FILE_STANDARD_INFO* standard_info,
ULONG *reparse_tag)
{
- memset(info, 0, sizeof(*info));
- info->dwFileAttributes = pFileData->dwFileAttributes;
- info->ftCreationTime = pFileData->ftCreationTime;
- info->ftLastAccessTime = pFileData->ftLastAccessTime;
- info->ftLastWriteTime = pFileData->ftLastWriteTime;
- info->nFileSizeHigh = pFileData->nFileSizeHigh;
- info->nFileSizeLow = pFileData->nFileSizeLow;
-/* info->nNumberOfLinks = 1; */
+ memset(basic_info, 0, sizeof(*basic_info));
+ memset(standard_info, 0, sizeof(*standard_info));
+
+ basic_info->FileAttributes = pFileData->dwFileAttributes;
+ basic_info->CreationTime.HighPart =
pFileData->ftCreationTime.dwHighDateTime;
+ basic_info->CreationTime.LowPart = pFileData->ftCreationTime.dwLowDateTime;
+ basic_info->LastAccessTime.HighPart =
pFileData->ftLastAccessTime.dwHighDateTime;
+ basic_info->LastAccessTime.LowPart =
pFileData->ftLastAccessTime.dwLowDateTime;
+ basic_info->LastWriteTime.HighPart =
pFileData->ftLastWriteTime.dwHighDateTime;
+ basic_info->LastWriteTime.LowPart =
pFileData->ftLastWriteTime.dwLowDateTime;
+ standard_info->EndOfFile.HighPart = pFileData->nFileSizeHigh;
+ standard_info->EndOfFile.LowPart = pFileData->nFileSizeLow;
+
if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
*reparse_tag = pFileData->dwReserved0;
else
@@ -2043,7 +2049,7 @@ find_data_to_file_info(WIN32_FIND_DATAW *pFileData,
}
static BOOL
-attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG
*reparse_tag)
+attributes_from_dir(LPCWSTR pszFile, FILE_BASIC_INFO* basic_info,
FILE_STANDARD_INFO* standard_info, ULONG* reparse_tag)
{
HANDLE hFindFile;
WIN32_FIND_DATAW FileData;
@@ -2074,7 +2080,7 @@ attributes_from_dir(LPCWSTR pszFile,
BY_HANDLE_FILE_INFORMATION *info, ULONG *re
return FALSE;
}
FindClose(hFindFile);
- find_data_to_file_info(&FileData, info, reparse_tag);
+ find_data_to_file_info(&FileData, basic_info, standard_info, reparse_tag);
return TRUE;
}
@@ -2108,10 +2114,9 @@ win32_xstat_slow_impl(const wchar_t *path, struct
_Py_stat_struct *result,
BOOL traverse)
{
HANDLE hFile;
- BY_HANDLE_FILE_INFORMATION fileInfo;
- FILE_BASIC_INFO basicInfo;
- FILE_BASIC_INFO *pBasicInfo = NULL;
- FILE_ID_INFO idInfo;
+ FILE_STANDARD_INFO standardInfo = {0};
+ FILE_BASIC_INFO basicInfo = {0};
+ FILE_ID_INFO idInfo = {0};
FILE_ID_INFO *pIdInfo = NULL;
FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 };
DWORD fileType, error;
@@ -2132,7 +2137,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct
_Py_stat_struct *result,
case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */
case ERROR_SHARING_VIOLATION: /* It's a paging file. */
/* Try reading the parent directory. */
- if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) {
+ if (!attributes_from_dir(path, &basicInfo, &standardInfo,
&tagInfo.ReparseTag)) {
/* Cannot read the parent directory. */
switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND: /* File cannot be found */
@@ -2147,7 +2152,7 @@ win32_xstat_slow_impl(const wchar_t *path, struct
_Py_stat_struct *result,
return -1;
}
- if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ if (basicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (traverse ||
!IsReparseTagNameSurrogate(tagInfo.ReparseTag)) {
/* The stat call has to traverse but cannot, so fail. */
@@ -2247,9 +2252,8 @@ win32_xstat_slow_impl(const wchar_t *path, struct
_Py_stat_struct *result,
}
}
- if (!GetFileInformationByHandle(hFile, &fileInfo) ||
- !GetFileInformationByHandleEx(hFile, FileBasicInfo,
- &basicInfo, sizeof(basicInfo))) {
+ if (!GetFileInformationByHandleEx(hFile, FileStandardInfo,
&standardInfo, sizeof(standardInfo)) ||
+ !GetFileInformationByHandleEx(hFile, FileBasicInfo, &basicInfo,
sizeof(basicInfo))) {
switch (GetLastError()) {
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_FUNCTION:
@@ -2264,17 +2268,14 @@ win32_xstat_slow_impl(const wchar_t *path, struct
_Py_stat_struct *result,
goto cleanup;
}
- /* Successfully got FileBasicInfo, so we'll pass it along */
- pBasicInfo = &basicInfo;
-
if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo,
sizeof(idInfo))) {
/* Successfully got FileIdInfo, so pass it along */
pIdInfo = &idInfo;
}
}
- _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo,
pIdInfo, result);
- update_st_mode_from_path(path, fileInfo.dwFileAttributes, result);
+ _Py_attribute_data_to_stat(&standardInfo, tagInfo.ReparseTag, &basicInfo,
pIdInfo, result);
+ update_st_mode_from_path(path, basicInfo.FileAttributes, result);
cleanup:
if (hFile != INVALID_HANDLE_VALUE) {
@@ -16702,7 +16703,8 @@ static PyObject *
DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW
*dataW)
{
DirEntry *entry;
- BY_HANDLE_FILE_INFORMATION file_info;
+ FILE_BASIC_INFO basic_info;
+ FILE_STANDARD_INFO standard_info;
ULONG reparse_tag;
wchar_t *joined_path;
@@ -16740,8 +16742,8 @@ DirEntry_from_find_data(PyObject *module, path_t *path,
WIN32_FIND_DATAW *dataW)
goto error;
}
- find_data_to_file_info(dataW, &file_info, &reparse_tag);
- _Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL,
&entry->win32_lstat);
+ find_data_to_file_info(dataW, &basic_info, &standard_info, &reparse_tag);
+ _Py_attribute_data_to_stat(&standard_info, reparse_tag, &basic_info, NULL,
&entry->win32_lstat);
/* ctime is only deprecated from 3.12, so we copy birthtime across */
entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime;
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 98ed66eff94ee1..5cbfd8e6ce4fa0 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -1103,50 +1103,41 @@ typedef union {
void
-_Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *info, ULONG reparse_tag,
+_Py_attribute_data_to_stat(FILE_STANDARD_INFO* standard_info, ULONG
reparse_tag,
FILE_BASIC_INFO *basic_info, FILE_ID_INFO *id_info,
struct _Py_stat_struct *result)
{
memset(result, 0, sizeof(*result));
- result->st_mode = attributes_to_mode(info->dwFileAttributes);
- result->st_size = (((__int64)info->nFileSizeHigh)<<32) +
info->nFileSizeLow;
- result->st_dev = id_info ? id_info->VolumeSerialNumber :
info->dwVolumeSerialNumber;
- result->st_rdev = 0;
+
+ result->st_size = standard_info->EndOfFile.QuadPart;
+ result->st_nlink = standard_info->NumberOfLinks;
+
/* st_ctime is deprecated, but we preserve the legacy value in our caller,
not here */
- if (basic_info) {
- LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime,
&result->st_birthtime, &result->st_birthtime_nsec);
- LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime,
&result->st_ctime, &result->st_ctime_nsec);
- LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime,
&result->st_mtime, &result->st_mtime_nsec);
- LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime,
&result->st_atime, &result->st_atime_nsec);
- } else {
- FILE_TIME_to_time_t_nsec(&info->ftCreationTime, &result->st_birthtime,
&result->st_birthtime_nsec);
- FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime, &result->st_mtime,
&result->st_mtime_nsec);
- FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime, &result->st_atime,
&result->st_atime_nsec);
- }
- result->st_nlink = info->nNumberOfLinks;
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->CreationTime,
&result->st_birthtime, &result->st_birthtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->ChangeTime, &result->st_ctime,
&result->st_ctime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->LastWriteTime,
&result->st_mtime, &result->st_mtime_nsec);
+ LARGE_INTEGER_to_time_t_nsec(&basic_info->LastAccessTime,
&result->st_atime, &result->st_atime_nsec);
if (id_info) {
+ result->st_dev = id_info->VolumeSerialNumber;
id_128_to_ino file_id;
file_id.id = id_info->FileId;
result->st_ino = file_id.st_ino;
result->st_ino_high = file_id.st_ino_high;
}
- if (!result->st_ino && !result->st_ino_high) {
- /* should only occur for DirEntry_from_find_data, in which case the
- index is likely to be zero anyway. */
- result->st_ino = (((uint64_t)info->nFileIndexHigh) << 32) +
info->nFileIndexLow;
- }
+
+ result->st_file_attributes = basic_info->FileAttributes;
+ result->st_mode = attributes_to_mode(result->st_file_attributes);
/* bpo-37834: Only actual symlinks set the S_IFLNK flag. But lstat() will
open other name surrogate reparse points without traversing them. To
detect/handle these, check st_file_attributes and st_reparse_tag. */
result->st_reparse_tag = reparse_tag;
- if (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
+ if (result->st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT &&
reparse_tag == IO_REPARSE_TAG_SYMLINK) {
/* set the bits that make this a symlink */
result->st_mode = (result->st_mode & ~S_IFMT) | S_IFLNK;
}
- result->st_file_attributes = info->dwFileAttributes;
}
void
@@ -1231,9 +1222,9 @@ int
_Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
{
#ifdef MS_WINDOWS
- BY_HANDLE_FILE_INFORMATION info;
- FILE_BASIC_INFO basicInfo;
- FILE_ID_INFO idInfo;
+ FILE_STANDARD_INFO standardInfo = {0};
+ FILE_BASIC_INFO basicInfo = {0};
+ FILE_ID_INFO idInfo = {0};
FILE_ID_INFO *pIdInfo = &idInfo;
HANDLE h;
int type;
@@ -1266,7 +1257,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
return 0;
}
- if (!GetFileInformationByHandle(h, &info) ||
+ if (!GetFileInformationByHandleEx(h,FileStandardInfo, &standardInfo,
sizeof(standardInfo)) ||
!GetFileInformationByHandleEx(h, FileBasicInfo, &basicInfo,
sizeof(basicInfo))) {
/* The Win32 error is already set, but we also set errno for
callers who expect it */
@@ -1279,7 +1270,7 @@ _Py_fstat_noraise(int fd, struct _Py_stat_struct *status)
pIdInfo = NULL;
}
- _Py_attribute_data_to_stat(&info, 0, &basicInfo, pIdInfo, status);
+ _Py_attribute_data_to_stat(&standardInfo, 0, &basicInfo, pIdInfo, status);
return 0;
#else
return fstat(fd, status);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]