##// END OF EJS Templates
osutil: fix the bug on OS X when we return more in listdir...
Maciej Fijalkowski -
r29821:8656dcac default
parent child Browse files
Show More
@@ -1,361 +1,362
1 # osutil.py - pure Python version of osutil.c
1 # osutil.py - pure Python version of osutil.c
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ctypes
10 import ctypes
11 import ctypes.util
11 import ctypes.util
12 import os
12 import os
13 import socket
13 import socket
14 import stat as statmod
14 import stat as statmod
15 import sys
15 import sys
16
16
17 from . import policy
17 from . import policy
18 modulepolicy = policy.policy
18 modulepolicy = policy.policy
19 policynocffi = policy.policynocffi
19 policynocffi = policy.policynocffi
20
20
21 def _mode_to_kind(mode):
21 def _mode_to_kind(mode):
22 if statmod.S_ISREG(mode):
22 if statmod.S_ISREG(mode):
23 return statmod.S_IFREG
23 return statmod.S_IFREG
24 if statmod.S_ISDIR(mode):
24 if statmod.S_ISDIR(mode):
25 return statmod.S_IFDIR
25 return statmod.S_IFDIR
26 if statmod.S_ISLNK(mode):
26 if statmod.S_ISLNK(mode):
27 return statmod.S_IFLNK
27 return statmod.S_IFLNK
28 if statmod.S_ISBLK(mode):
28 if statmod.S_ISBLK(mode):
29 return statmod.S_IFBLK
29 return statmod.S_IFBLK
30 if statmod.S_ISCHR(mode):
30 if statmod.S_ISCHR(mode):
31 return statmod.S_IFCHR
31 return statmod.S_IFCHR
32 if statmod.S_ISFIFO(mode):
32 if statmod.S_ISFIFO(mode):
33 return statmod.S_IFIFO
33 return statmod.S_IFIFO
34 if statmod.S_ISSOCK(mode):
34 if statmod.S_ISSOCK(mode):
35 return statmod.S_IFSOCK
35 return statmod.S_IFSOCK
36 return mode
36 return mode
37
37
38 def listdirpure(path, stat=False, skip=None):
38 def listdirpure(path, stat=False, skip=None):
39 '''listdir(path, stat=False) -> list_of_tuples
39 '''listdir(path, stat=False) -> list_of_tuples
40
40
41 Return a sorted list containing information about the entries
41 Return a sorted list containing information about the entries
42 in the directory.
42 in the directory.
43
43
44 If stat is True, each element is a 3-tuple:
44 If stat is True, each element is a 3-tuple:
45
45
46 (name, type, stat object)
46 (name, type, stat object)
47
47
48 Otherwise, each element is a 2-tuple:
48 Otherwise, each element is a 2-tuple:
49
49
50 (name, type)
50 (name, type)
51 '''
51 '''
52 result = []
52 result = []
53 prefix = path
53 prefix = path
54 if not prefix.endswith(os.sep):
54 if not prefix.endswith(os.sep):
55 prefix += os.sep
55 prefix += os.sep
56 names = os.listdir(path)
56 names = os.listdir(path)
57 names.sort()
57 names.sort()
58 for fn in names:
58 for fn in names:
59 st = os.lstat(prefix + fn)
59 st = os.lstat(prefix + fn)
60 if fn == skip and statmod.S_ISDIR(st.st_mode):
60 if fn == skip and statmod.S_ISDIR(st.st_mode):
61 return []
61 return []
62 if stat:
62 if stat:
63 result.append((fn, _mode_to_kind(st.st_mode), st))
63 result.append((fn, _mode_to_kind(st.st_mode), st))
64 else:
64 else:
65 result.append((fn, _mode_to_kind(st.st_mode)))
65 result.append((fn, _mode_to_kind(st.st_mode)))
66 return result
66 return result
67
67
68 ffi = None
68 ffi = None
69 if modulepolicy not in policynocffi and sys.platform == 'darwin':
69 if modulepolicy not in policynocffi and sys.platform == 'darwin':
70 try:
70 try:
71 from _osutil_cffi import ffi, lib
71 from _osutil_cffi import ffi, lib
72 except ImportError:
72 except ImportError:
73 if modulepolicy == 'cffi': # strict cffi import
73 if modulepolicy == 'cffi': # strict cffi import
74 raise
74 raise
75
75
76 if sys.platform == 'darwin' and ffi is not None:
76 if sys.platform == 'darwin' and ffi is not None:
77 listdir_batch_size = 4096
77 listdir_batch_size = 4096
78 # tweakable number, only affects performance, which chunks
78 # tweakable number, only affects performance, which chunks
79 # of bytes do we get back from getattrlistbulk
79 # of bytes do we get back from getattrlistbulk
80
80
81 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
81 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty
82
82
83 attrkinds[lib.VREG] = statmod.S_IFREG
83 attrkinds[lib.VREG] = statmod.S_IFREG
84 attrkinds[lib.VDIR] = statmod.S_IFDIR
84 attrkinds[lib.VDIR] = statmod.S_IFDIR
85 attrkinds[lib.VLNK] = statmod.S_IFLNK
85 attrkinds[lib.VLNK] = statmod.S_IFLNK
86 attrkinds[lib.VBLK] = statmod.S_IFBLK
86 attrkinds[lib.VBLK] = statmod.S_IFBLK
87 attrkinds[lib.VCHR] = statmod.S_IFCHR
87 attrkinds[lib.VCHR] = statmod.S_IFCHR
88 attrkinds[lib.VFIFO] = statmod.S_IFIFO
88 attrkinds[lib.VFIFO] = statmod.S_IFIFO
89 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
89 attrkinds[lib.VSOCK] = statmod.S_IFSOCK
90
90
91 class stat_res(object):
91 class stat_res(object):
92 def __init__(self, st_mode, st_mtime, st_size):
92 def __init__(self, st_mode, st_mtime, st_size):
93 self.st_mode = st_mode
93 self.st_mode = st_mode
94 self.st_mtime = st_mtime
94 self.st_mtime = st_mtime
95 self.st_size = st_size
95 self.st_size = st_size
96
96
97 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
97 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
98 buf = ffi.new("char[]", listdir_batch_size)
98 buf = ffi.new("char[]", listdir_batch_size)
99
99
100 def listdirinternal(dfd, req, stat, skip):
100 def listdirinternal(dfd, req, stat, skip):
101 ret = []
101 ret = []
102 while True:
102 while True:
103 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
103 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
104 if r == 0:
104 if r == 0:
105 break
105 break
106 if r == -1:
106 if r == -1:
107 raise OSError(ffi.errno, os.strerror(ffi.errno))
107 raise OSError(ffi.errno, os.strerror(ffi.errno))
108 cur = ffi.cast("val_attrs_t*", buf)
108 cur = ffi.cast("val_attrs_t*", buf)
109 for i in range(r):
109 for i in range(r):
110 lgt = cur.length
110 lgt = cur.length
111 assert lgt == ffi.cast('uint32_t*', cur)[0]
111 assert lgt == ffi.cast('uint32_t*', cur)[0]
112 ofs = cur.name_info.attr_dataoffset
112 ofs = cur.name_info.attr_dataoffset
113 str_lgt = cur.name_info.attr_length
113 str_lgt = cur.name_info.attr_length
114 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
114 base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
115 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
115 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
116 str_lgt - 1))
116 str_lgt - 1))
117 tp = attrkinds[cur.obj_type]
117 tp = attrkinds[cur.obj_type]
118 if name == "." or name == "..":
118 if name == "." or name == "..":
119 continue
119 continue
120 if skip == name and tp == statmod.S_ISDIR:
120 if skip == name and tp == statmod.S_ISDIR:
121 return []
121 return []
122 if stat:
122 if stat:
123 mtime = cur.time.tv_sec
123 mtime = cur.mtime.tv_sec
124 mode = (cur.accessmask & ~lib.S_IFMT)| tp
124 mode = (cur.accessmask & ~lib.S_IFMT)| tp
125 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
125 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
126 st_size=cur.datalength)))
126 st_size=cur.datalength)))
127 else:
127 else:
128 ret.append((name, tp))
128 ret.append((name, tp))
129 cur += lgt
129 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
130 + lgt)
130 return ret
131 return ret
131
132
132 def listdir(path, stat=False, skip=None):
133 def listdir(path, stat=False, skip=None):
133 req = ffi.new("struct attrlist*")
134 req = ffi.new("struct attrlist*")
134 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
135 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
135 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
136 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
136 lib.ATTR_CMN_NAME |
137 lib.ATTR_CMN_NAME |
137 lib.ATTR_CMN_OBJTYPE |
138 lib.ATTR_CMN_OBJTYPE |
138 lib.ATTR_CMN_ACCESSMASK |
139 lib.ATTR_CMN_ACCESSMASK |
139 lib.ATTR_CMN_MODTIME)
140 lib.ATTR_CMN_MODTIME)
140 req.fileattr = lib.ATTR_FILE_DATALENGTH
141 req.fileattr = lib.ATTR_FILE_DATALENGTH
141 dfd = lib.open(path, lib.O_RDONLY, 0)
142 dfd = lib.open(path, lib.O_RDONLY, 0)
142 if dfd == -1:
143 if dfd == -1:
143 raise OSError(ffi.errno, os.strerror(ffi.errno))
144 raise OSError(ffi.errno, os.strerror(ffi.errno))
144
145
145 try:
146 try:
146 ret = listdirinternal(dfd, req, stat, skip)
147 ret = listdirinternal(dfd, req, stat, skip)
147 finally:
148 finally:
148 try:
149 try:
149 lib.close(dfd)
150 lib.close(dfd)
150 except BaseException:
151 except BaseException:
151 pass # we ignore all the errors from closing, not
152 pass # we ignore all the errors from closing, not
152 # much we can do about that
153 # much we can do about that
153 return ret
154 return ret
154 else:
155 else:
155 listdir = listdirpure
156 listdir = listdirpure
156
157
157 if os.name != 'nt':
158 if os.name != 'nt':
158 posixfile = open
159 posixfile = open
159
160
160 _SCM_RIGHTS = 0x01
161 _SCM_RIGHTS = 0x01
161 _socklen_t = ctypes.c_uint
162 _socklen_t = ctypes.c_uint
162
163
163 if sys.platform == 'linux2':
164 if sys.platform == 'linux2':
164 # socket.h says "the type should be socklen_t but the definition of
165 # socket.h says "the type should be socklen_t but the definition of
165 # the kernel is incompatible with this."
166 # the kernel is incompatible with this."
166 _cmsg_len_t = ctypes.c_size_t
167 _cmsg_len_t = ctypes.c_size_t
167 _msg_controllen_t = ctypes.c_size_t
168 _msg_controllen_t = ctypes.c_size_t
168 _msg_iovlen_t = ctypes.c_size_t
169 _msg_iovlen_t = ctypes.c_size_t
169 else:
170 else:
170 _cmsg_len_t = _socklen_t
171 _cmsg_len_t = _socklen_t
171 _msg_controllen_t = _socklen_t
172 _msg_controllen_t = _socklen_t
172 _msg_iovlen_t = ctypes.c_int
173 _msg_iovlen_t = ctypes.c_int
173
174
174 class _iovec(ctypes.Structure):
175 class _iovec(ctypes.Structure):
175 _fields_ = [
176 _fields_ = [
176 (u'iov_base', ctypes.c_void_p),
177 (u'iov_base', ctypes.c_void_p),
177 (u'iov_len', ctypes.c_size_t),
178 (u'iov_len', ctypes.c_size_t),
178 ]
179 ]
179
180
180 class _msghdr(ctypes.Structure):
181 class _msghdr(ctypes.Structure):
181 _fields_ = [
182 _fields_ = [
182 (u'msg_name', ctypes.c_void_p),
183 (u'msg_name', ctypes.c_void_p),
183 (u'msg_namelen', _socklen_t),
184 (u'msg_namelen', _socklen_t),
184 (u'msg_iov', ctypes.POINTER(_iovec)),
185 (u'msg_iov', ctypes.POINTER(_iovec)),
185 (u'msg_iovlen', _msg_iovlen_t),
186 (u'msg_iovlen', _msg_iovlen_t),
186 (u'msg_control', ctypes.c_void_p),
187 (u'msg_control', ctypes.c_void_p),
187 (u'msg_controllen', _msg_controllen_t),
188 (u'msg_controllen', _msg_controllen_t),
188 (u'msg_flags', ctypes.c_int),
189 (u'msg_flags', ctypes.c_int),
189 ]
190 ]
190
191
191 class _cmsghdr(ctypes.Structure):
192 class _cmsghdr(ctypes.Structure):
192 _fields_ = [
193 _fields_ = [
193 (u'cmsg_len', _cmsg_len_t),
194 (u'cmsg_len', _cmsg_len_t),
194 (u'cmsg_level', ctypes.c_int),
195 (u'cmsg_level', ctypes.c_int),
195 (u'cmsg_type', ctypes.c_int),
196 (u'cmsg_type', ctypes.c_int),
196 (u'cmsg_data', ctypes.c_ubyte * 0),
197 (u'cmsg_data', ctypes.c_ubyte * 0),
197 ]
198 ]
198
199
199 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
200 _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True)
200 _recvmsg = getattr(_libc, 'recvmsg', None)
201 _recvmsg = getattr(_libc, 'recvmsg', None)
201 if _recvmsg:
202 if _recvmsg:
202 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
203 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
203 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
204 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr),
204 ctypes.c_int)
205 ctypes.c_int)
205 else:
206 else:
206 # recvmsg isn't always provided by libc; such systems are unsupported
207 # recvmsg isn't always provided by libc; such systems are unsupported
207 def _recvmsg(sockfd, msg, flags):
208 def _recvmsg(sockfd, msg, flags):
208 raise NotImplementedError('unsupported platform')
209 raise NotImplementedError('unsupported platform')
209
210
210 def _CMSG_FIRSTHDR(msgh):
211 def _CMSG_FIRSTHDR(msgh):
211 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
212 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
212 return
213 return
213 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
214 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
214 return cmsgptr.contents
215 return cmsgptr.contents
215
216
216 # The pure version is less portable than the native version because the
217 # The pure version is less portable than the native version because the
217 # handling of socket ancillary data heavily depends on C preprocessor.
218 # handling of socket ancillary data heavily depends on C preprocessor.
218 # Also, some length fields are wrongly typed in Linux kernel.
219 # Also, some length fields are wrongly typed in Linux kernel.
219 def recvfds(sockfd):
220 def recvfds(sockfd):
220 """receive list of file descriptors via socket"""
221 """receive list of file descriptors via socket"""
221 dummy = (ctypes.c_ubyte * 1)()
222 dummy = (ctypes.c_ubyte * 1)()
222 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
223 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
223 cbuf = ctypes.create_string_buffer(256)
224 cbuf = ctypes.create_string_buffer(256)
224 msgh = _msghdr(None, 0,
225 msgh = _msghdr(None, 0,
225 ctypes.pointer(iov), 1,
226 ctypes.pointer(iov), 1,
226 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
227 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
227 0)
228 0)
228 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
229 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
229 if r < 0:
230 if r < 0:
230 e = ctypes.get_errno()
231 e = ctypes.get_errno()
231 raise OSError(e, os.strerror(e))
232 raise OSError(e, os.strerror(e))
232 # assumes that the first cmsg has fds because it isn't easy to write
233 # assumes that the first cmsg has fds because it isn't easy to write
233 # portable CMSG_NXTHDR() with ctypes.
234 # portable CMSG_NXTHDR() with ctypes.
234 cmsg = _CMSG_FIRSTHDR(msgh)
235 cmsg = _CMSG_FIRSTHDR(msgh)
235 if not cmsg:
236 if not cmsg:
236 return []
237 return []
237 if (cmsg.cmsg_level != socket.SOL_SOCKET or
238 if (cmsg.cmsg_level != socket.SOL_SOCKET or
238 cmsg.cmsg_type != _SCM_RIGHTS):
239 cmsg.cmsg_type != _SCM_RIGHTS):
239 return []
240 return []
240 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
241 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
241 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
242 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
242 ctypes.sizeof(ctypes.c_int))
243 ctypes.sizeof(ctypes.c_int))
243 return [rfds[i] for i in xrange(rfdscount)]
244 return [rfds[i] for i in xrange(rfdscount)]
244
245
245 else:
246 else:
246 import msvcrt
247 import msvcrt
247
248
248 _kernel32 = ctypes.windll.kernel32
249 _kernel32 = ctypes.windll.kernel32
249
250
250 _DWORD = ctypes.c_ulong
251 _DWORD = ctypes.c_ulong
251 _LPCSTR = _LPSTR = ctypes.c_char_p
252 _LPCSTR = _LPSTR = ctypes.c_char_p
252 _HANDLE = ctypes.c_void_p
253 _HANDLE = ctypes.c_void_p
253
254
254 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
255 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
255
256
256 # CreateFile
257 # CreateFile
257 _FILE_SHARE_READ = 0x00000001
258 _FILE_SHARE_READ = 0x00000001
258 _FILE_SHARE_WRITE = 0x00000002
259 _FILE_SHARE_WRITE = 0x00000002
259 _FILE_SHARE_DELETE = 0x00000004
260 _FILE_SHARE_DELETE = 0x00000004
260
261
261 _CREATE_ALWAYS = 2
262 _CREATE_ALWAYS = 2
262 _OPEN_EXISTING = 3
263 _OPEN_EXISTING = 3
263 _OPEN_ALWAYS = 4
264 _OPEN_ALWAYS = 4
264
265
265 _GENERIC_READ = 0x80000000
266 _GENERIC_READ = 0x80000000
266 _GENERIC_WRITE = 0x40000000
267 _GENERIC_WRITE = 0x40000000
267
268
268 _FILE_ATTRIBUTE_NORMAL = 0x80
269 _FILE_ATTRIBUTE_NORMAL = 0x80
269
270
270 # open_osfhandle flags
271 # open_osfhandle flags
271 _O_RDONLY = 0x0000
272 _O_RDONLY = 0x0000
272 _O_RDWR = 0x0002
273 _O_RDWR = 0x0002
273 _O_APPEND = 0x0008
274 _O_APPEND = 0x0008
274
275
275 _O_TEXT = 0x4000
276 _O_TEXT = 0x4000
276 _O_BINARY = 0x8000
277 _O_BINARY = 0x8000
277
278
278 # types of parameters of C functions used (required by pypy)
279 # types of parameters of C functions used (required by pypy)
279
280
280 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
281 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
281 _DWORD, _DWORD, _HANDLE]
282 _DWORD, _DWORD, _HANDLE]
282 _kernel32.CreateFileA.restype = _HANDLE
283 _kernel32.CreateFileA.restype = _HANDLE
283
284
284 def _raiseioerror(name):
285 def _raiseioerror(name):
285 err = ctypes.WinError()
286 err = ctypes.WinError()
286 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
287 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
287
288
288 class posixfile(object):
289 class posixfile(object):
289 '''a file object aiming for POSIX-like semantics
290 '''a file object aiming for POSIX-like semantics
290
291
291 CPython's open() returns a file that was opened *without* setting the
292 CPython's open() returns a file that was opened *without* setting the
292 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
293 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
293 This even happens if any hardlinked copy of the file is in open state.
294 This even happens if any hardlinked copy of the file is in open state.
294 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
295 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
295 renamed and deleted while they are held open.
296 renamed and deleted while they are held open.
296 Note that if a file opened with posixfile is unlinked, the file
297 Note that if a file opened with posixfile is unlinked, the file
297 remains but cannot be opened again or be recreated under the same name,
298 remains but cannot be opened again or be recreated under the same name,
298 until all reading processes have closed the file.'''
299 until all reading processes have closed the file.'''
299
300
300 def __init__(self, name, mode='r', bufsize=-1):
301 def __init__(self, name, mode='r', bufsize=-1):
301 if 'b' in mode:
302 if 'b' in mode:
302 flags = _O_BINARY
303 flags = _O_BINARY
303 else:
304 else:
304 flags = _O_TEXT
305 flags = _O_TEXT
305
306
306 m0 = mode[0]
307 m0 = mode[0]
307 if m0 == 'r' and '+' not in mode:
308 if m0 == 'r' and '+' not in mode:
308 flags |= _O_RDONLY
309 flags |= _O_RDONLY
309 access = _GENERIC_READ
310 access = _GENERIC_READ
310 else:
311 else:
311 # work around http://support.microsoft.com/kb/899149 and
312 # work around http://support.microsoft.com/kb/899149 and
312 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
313 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
313 flags |= _O_RDWR
314 flags |= _O_RDWR
314 access = _GENERIC_READ | _GENERIC_WRITE
315 access = _GENERIC_READ | _GENERIC_WRITE
315
316
316 if m0 == 'r':
317 if m0 == 'r':
317 creation = _OPEN_EXISTING
318 creation = _OPEN_EXISTING
318 elif m0 == 'w':
319 elif m0 == 'w':
319 creation = _CREATE_ALWAYS
320 creation = _CREATE_ALWAYS
320 elif m0 == 'a':
321 elif m0 == 'a':
321 creation = _OPEN_ALWAYS
322 creation = _OPEN_ALWAYS
322 flags |= _O_APPEND
323 flags |= _O_APPEND
323 else:
324 else:
324 raise ValueError("invalid mode: %s" % mode)
325 raise ValueError("invalid mode: %s" % mode)
325
326
326 fh = _kernel32.CreateFileA(name, access,
327 fh = _kernel32.CreateFileA(name, access,
327 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
328 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
328 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
329 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
329 if fh == _INVALID_HANDLE_VALUE:
330 if fh == _INVALID_HANDLE_VALUE:
330 _raiseioerror(name)
331 _raiseioerror(name)
331
332
332 fd = msvcrt.open_osfhandle(fh, flags)
333 fd = msvcrt.open_osfhandle(fh, flags)
333 if fd == -1:
334 if fd == -1:
334 _kernel32.CloseHandle(fh)
335 _kernel32.CloseHandle(fh)
335 _raiseioerror(name)
336 _raiseioerror(name)
336
337
337 f = os.fdopen(fd, mode, bufsize)
338 f = os.fdopen(fd, mode, bufsize)
338 # unfortunately, f.name is '<fdopen>' at this point -- so we store
339 # unfortunately, f.name is '<fdopen>' at this point -- so we store
339 # the name on this wrapper. We cannot just assign to f.name,
340 # the name on this wrapper. We cannot just assign to f.name,
340 # because that attribute is read-only.
341 # because that attribute is read-only.
341 object.__setattr__(self, 'name', name)
342 object.__setattr__(self, 'name', name)
342 object.__setattr__(self, '_file', f)
343 object.__setattr__(self, '_file', f)
343
344
344 def __iter__(self):
345 def __iter__(self):
345 return self._file
346 return self._file
346
347
347 def __getattr__(self, name):
348 def __getattr__(self, name):
348 return getattr(self._file, name)
349 return getattr(self._file, name)
349
350
350 def __setattr__(self, name, value):
351 def __setattr__(self, name, value):
351 '''mimics the read-only attributes of Python file objects
352 '''mimics the read-only attributes of Python file objects
352 by raising 'TypeError: readonly attribute' if someone tries:
353 by raising 'TypeError: readonly attribute' if someone tries:
353 f = posixfile('foo.txt')
354 f = posixfile('foo.txt')
354 f.name = 'bla' '''
355 f.name = 'bla' '''
355 return self._file.__setattr__(name, value)
356 return self._file.__setattr__(name, value)
356
357
357 def __enter__(self):
358 def __enter__(self):
358 return self._file.__enter__()
359 return self._file.__enter__()
359
360
360 def __exit__(self, exc_type, exc_value, exc_tb):
361 def __exit__(self, exc_type, exc_value, exc_tb):
361 return self._file.__exit__(exc_type, exc_value, exc_tb)
362 return self._file.__exit__(exc_type, exc_value, exc_tb)
General Comments 0
You need to be logged in to leave comments. Login now