1+ import ctypes
2+ import ctypes.wintypes
3+ import errno as errno_mod
4+ import msvcrt
15import os
26import platform
37
8+ from .base import SyncFile as BaseSyncFile
9+
410
511cdef extern from ' windows.h' :
612 ctypedef void * HANDLE
@@ -13,6 +19,89 @@ cdef extern from 'windows.h':
1319 cdef extern int PROCESS_QUERY_INFORMATION
1420
1521
22+ # Win32 API constants for CreateFileW
23+ GENERIC_WRITE = 0x40000000
24+ FILE_SHARE_READ = 0x00000001
25+ CREATE_NEW = 1
26+ FILE_ATTRIBUTE_NORMAL = 0x80
27+ FILE_FLAG_WRITE_THROUGH = 0x80000000
28+ ERROR_FILE_EXISTS = 80
29+
30+ _kernel32 = ctypes.WinDLL(" kernel32" , use_last_error = True )
31+ _CreateFileW = _kernel32.CreateFileW
32+ _CreateFileW.restype = ctypes.wintypes.HANDLE
33+ _CreateFileW.argtypes = [
34+ ctypes.wintypes.LPCWSTR,
35+ ctypes.wintypes.DWORD,
36+ ctypes.wintypes.DWORD,
37+ ctypes.c_void_p,
38+ ctypes.wintypes.DWORD,
39+ ctypes.wintypes.DWORD,
40+ ctypes.wintypes.HANDLE,
41+ ]
42+ _CloseHandle = _kernel32.CloseHandle
43+ INVALID_HANDLE_VALUE = ctypes.wintypes.HANDLE(- 1 ).value
44+
45+
46+ class SyncFile (BaseSyncFile ):
47+ """
48+ Windows SyncFile using FILE_FLAG_WRITE_THROUGH for data durability.
49+
50+ FILE_FLAG_WRITE_THROUGH instructs Windows to write through any intermediate
51+ cache and go directly to disk, providing data durability guarantees similar
52+ to fdatasync/F_FULLFSYNC on POSIX/macOS systems.
53+
54+ When an already-open fd is provided, falls back to base implementation.
55+ """
56+
57+ def __init__ (self , path , *, fd = None , binary = False ):
58+ if fd is not None :
59+ # An already-opened fd was provided (e.g., from SaveFile via mkstemp).
60+ # We cannot change its flags, so fall back to the base implementation.
61+ super ().__init__(path, fd = fd, binary = binary)
62+ return
63+
64+ self .path = path
65+ handle = _CreateFileW(
66+ str (path),
67+ GENERIC_WRITE,
68+ FILE_SHARE_READ,
69+ None ,
70+ CREATE_NEW, # fail if file exists, matching Python's 'x' mode
71+ FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
72+ None ,
73+ )
74+ if handle == INVALID_HANDLE_VALUE:
75+ error = ctypes.get_last_error()
76+ if error == ERROR_FILE_EXISTS:
77+ raise FileExistsError(errno_mod.EEXIST, os.strerror(errno_mod.EEXIST), str (path))
78+ raise ctypes.WinError(error)
79+
80+ try :
81+ oflags = os.O_BINARY if binary else os.O_TEXT
82+ c_fd = msvcrt.open_osfhandle(handle, oflags)
83+ except Exception :
84+ _CloseHandle(handle)
85+ raise
86+
87+ try :
88+ mode = " wb" if binary else " w"
89+ self .f = os.fdopen(c_fd, mode = mode)
90+ except Exception :
91+ os.close(c_fd) # Also closes the underlying Windows handle
92+ raise
93+ self .fd = self .f.fileno()
94+
95+ def sync (self ):
96+ """ Flush and sync to persistent storage.
97+
98+ With FILE_FLAG_WRITE_THROUGH, writes already go to stable storage.
99+ We still call os.fsync (FlushFileBuffers) for belt-and-suspenders safety.
100+ """
101+ self .f.flush()
102+ os.fsync(self .fd)
103+
104+
16105def getosusername ():
17106 """ Return the OS username."""
18107 return os.getlogin()
0 commit comments