##// END OF EJS Templates
pure: provide more correct implementation of posixfile for Windows...
Adrian Buehlmann -
r14413:5ef18e28 default
parent child Browse files
Show More
@@ -8,8 +8,6 b''
8 import os
8 import os
9 import stat as statmod
9 import stat as statmod
10
10
11 posixfile = open
12
13 def _mode_to_kind(mode):
11 def _mode_to_kind(mode):
14 if statmod.S_ISREG(mode):
12 if statmod.S_ISREG(mode):
15 return statmod.S_IFREG
13 return statmod.S_IFREG
@@ -57,3 +55,131 b' def listdir(path, stat=False, skip=None)'
57 result.append((fn, _mode_to_kind(st.st_mode)))
55 result.append((fn, _mode_to_kind(st.st_mode)))
58 return result
56 return result
59
57
58 if os.name != 'nt':
59 posixfile = open
60 else:
61 import ctypes, ctypes.util
62
63 _kernel32 = ctypes.windll.kernel32
64
65 _DWORD = ctypes.c_ulong
66 _LPCSTR = _LPSTR = ctypes.c_char_p
67 _HANDLE = ctypes.c_void_p
68
69 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
70
71 def _crtname():
72 try:
73 # find_msvcrt was introduced in Python 2.6
74 return ctypes.util.find_msvcrt()
75 except AttributeError:
76 return 'msvcr80.dll' # CPython 2.5
77
78 _crt = ctypes.PyDLL(_crtname())
79
80 # CreateFile
81 _FILE_SHARE_READ = 0x00000001
82 _FILE_SHARE_WRITE = 0x00000002
83 _FILE_SHARE_DELETE = 0x00000004
84
85 _CREATE_ALWAYS = 2
86 _OPEN_EXISTING = 3
87 _OPEN_ALWAYS = 4
88
89 _GENERIC_READ = 0x80000000
90 _GENERIC_WRITE = 0x40000000
91
92 _FILE_ATTRIBUTE_NORMAL = 0x80
93
94 # _open_osfhandle
95 _O_RDONLY = 0x0000
96 _O_RDWR = 0x0002
97 _O_APPEND = 0x0008
98
99 _O_TEXT = 0x4000
100 _O_BINARY = 0x8000
101
102 # types of parameters of C functions used (required by pypy)
103
104 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
105 _DWORD, _DWORD, _HANDLE]
106 _kernel32.CreateFileA.restype = _HANDLE
107
108 _crt._open_osfhandle.argtypes = [_HANDLE, ctypes.c_int]
109 _crt._open_osfhandle.restype = ctypes.c_int
110
111 def _raiseioerror(name):
112 err = ctypes.WinError()
113 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
114
115 class posixfile(object):
116 '''a file object aiming for POSIX-like semantics
117
118 CPython's open() returns a file that was opened *without* setting the
119 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
120 This even happens if any hardlinked copy of the file is in open state.
121 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
122 renamed and deleted while they are held open.
123 Note that if a file opened with posixfile is unlinked, the file
124 remains but cannot be opened again or be recreated under the same name,
125 until all reading processes have closed the file.'''
126
127 def __init__(self, name, mode='r', bufsize=-1):
128 if 'b' in mode:
129 flags = _O_BINARY
130 else:
131 flags = _O_TEXT
132
133 m0 = mode[0]
134 if m0 == 'r' and not '+' in mode:
135 flags |= _O_RDONLY
136 access = _GENERIC_READ
137 else:
138 # work around http://support.microsoft.com/kb/899149 and
139 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
140 flags |= _O_RDWR
141 access = _GENERIC_READ | _GENERIC_WRITE
142
143 if m0 == 'r':
144 creation = _OPEN_EXISTING
145 elif m0 == 'w':
146 creation = _CREATE_ALWAYS
147 elif m0 == 'a':
148 creation = _OPEN_ALWAYS
149 flags |= _O_APPEND
150 else:
151 raise ValueError("invalid mode: %s" % mode)
152
153 fh = _kernel32.CreateFileA(name, access,
154 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
155 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
156 if fh == _INVALID_HANDLE_VALUE:
157 _raiseioerror(name)
158
159 # for CPython we must use the same CRT as Python uses,
160 # or the os.fdopen call below will abort with
161 # "OSError: [Errno 9] Bad file descriptor"
162 fd = _crt._open_osfhandle(fh, flags)
163 if fd == -1:
164 _kernel32.CloseHandle(fh)
165 _raiseioerror(name)
166
167 f = os.fdopen(fd, mode, bufsize)
168 # unfortunately, f.name is '<fdopen>' at this point -- so we store
169 # the name on this wrapper. We cannot just assign to f.name,
170 # because that attribute is read-only.
171 object.__setattr__(self, 'name', name)
172 object.__setattr__(self, '_file', f)
173
174 def __iter__(self):
175 return self._file
176
177 def __getattr__(self, name):
178 return getattr(self._file, name)
179
180 def __setattr__(self, name, value):
181 '''mimics the read-only attributes of Python file objects
182 by raising 'TypeError: readonly attribute' if someone tries:
183 f = posixfile('foo.txt')
184 f.name = 'bla' '''
185 return self._file.__setattr__(name, value)
General Comments 0
You need to be logged in to leave comments. Login now