##// END OF EJS Templates
osutil: implement __enter__ and __exit__ on posixfile...
Gregory Szorc -
r27704:051b0dce default
parent child Browse files
Show More
@@ -1,256 +1,262 b''
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 def _mode_to_kind(mode):
17 def _mode_to_kind(mode):
18 if statmod.S_ISREG(mode):
18 if statmod.S_ISREG(mode):
19 return statmod.S_IFREG
19 return statmod.S_IFREG
20 if statmod.S_ISDIR(mode):
20 if statmod.S_ISDIR(mode):
21 return statmod.S_IFDIR
21 return statmod.S_IFDIR
22 if statmod.S_ISLNK(mode):
22 if statmod.S_ISLNK(mode):
23 return statmod.S_IFLNK
23 return statmod.S_IFLNK
24 if statmod.S_ISBLK(mode):
24 if statmod.S_ISBLK(mode):
25 return statmod.S_IFBLK
25 return statmod.S_IFBLK
26 if statmod.S_ISCHR(mode):
26 if statmod.S_ISCHR(mode):
27 return statmod.S_IFCHR
27 return statmod.S_IFCHR
28 if statmod.S_ISFIFO(mode):
28 if statmod.S_ISFIFO(mode):
29 return statmod.S_IFIFO
29 return statmod.S_IFIFO
30 if statmod.S_ISSOCK(mode):
30 if statmod.S_ISSOCK(mode):
31 return statmod.S_IFSOCK
31 return statmod.S_IFSOCK
32 return mode
32 return mode
33
33
34 def listdir(path, stat=False, skip=None):
34 def listdir(path, stat=False, skip=None):
35 '''listdir(path, stat=False) -> list_of_tuples
35 '''listdir(path, stat=False) -> list_of_tuples
36
36
37 Return a sorted list containing information about the entries
37 Return a sorted list containing information about the entries
38 in the directory.
38 in the directory.
39
39
40 If stat is True, each element is a 3-tuple:
40 If stat is True, each element is a 3-tuple:
41
41
42 (name, type, stat object)
42 (name, type, stat object)
43
43
44 Otherwise, each element is a 2-tuple:
44 Otherwise, each element is a 2-tuple:
45
45
46 (name, type)
46 (name, type)
47 '''
47 '''
48 result = []
48 result = []
49 prefix = path
49 prefix = path
50 if not prefix.endswith(os.sep):
50 if not prefix.endswith(os.sep):
51 prefix += os.sep
51 prefix += os.sep
52 names = os.listdir(path)
52 names = os.listdir(path)
53 names.sort()
53 names.sort()
54 for fn in names:
54 for fn in names:
55 st = os.lstat(prefix + fn)
55 st = os.lstat(prefix + fn)
56 if fn == skip and statmod.S_ISDIR(st.st_mode):
56 if fn == skip and statmod.S_ISDIR(st.st_mode):
57 return []
57 return []
58 if stat:
58 if stat:
59 result.append((fn, _mode_to_kind(st.st_mode), st))
59 result.append((fn, _mode_to_kind(st.st_mode), st))
60 else:
60 else:
61 result.append((fn, _mode_to_kind(st.st_mode)))
61 result.append((fn, _mode_to_kind(st.st_mode)))
62 return result
62 return result
63
63
64 if os.name != 'nt':
64 if os.name != 'nt':
65 posixfile = open
65 posixfile = open
66
66
67 _SCM_RIGHTS = 0x01
67 _SCM_RIGHTS = 0x01
68 _socklen_t = ctypes.c_uint
68 _socklen_t = ctypes.c_uint
69
69
70 if sys.platform == 'linux2':
70 if sys.platform == 'linux2':
71 # socket.h says "the type should be socklen_t but the definition of
71 # socket.h says "the type should be socklen_t but the definition of
72 # the kernel is incompatible with this."
72 # the kernel is incompatible with this."
73 _cmsg_len_t = ctypes.c_size_t
73 _cmsg_len_t = ctypes.c_size_t
74 _msg_controllen_t = ctypes.c_size_t
74 _msg_controllen_t = ctypes.c_size_t
75 _msg_iovlen_t = ctypes.c_size_t
75 _msg_iovlen_t = ctypes.c_size_t
76 else:
76 else:
77 _cmsg_len_t = _socklen_t
77 _cmsg_len_t = _socklen_t
78 _msg_controllen_t = _socklen_t
78 _msg_controllen_t = _socklen_t
79 _msg_iovlen_t = ctypes.c_int
79 _msg_iovlen_t = ctypes.c_int
80
80
81 class _iovec(ctypes.Structure):
81 class _iovec(ctypes.Structure):
82 _fields_ = [
82 _fields_ = [
83 ('iov_base', ctypes.c_void_p),
83 ('iov_base', ctypes.c_void_p),
84 ('iov_len', ctypes.c_size_t),
84 ('iov_len', ctypes.c_size_t),
85 ]
85 ]
86
86
87 class _msghdr(ctypes.Structure):
87 class _msghdr(ctypes.Structure):
88 _fields_ = [
88 _fields_ = [
89 ('msg_name', ctypes.c_void_p),
89 ('msg_name', ctypes.c_void_p),
90 ('msg_namelen', _socklen_t),
90 ('msg_namelen', _socklen_t),
91 ('msg_iov', ctypes.POINTER(_iovec)),
91 ('msg_iov', ctypes.POINTER(_iovec)),
92 ('msg_iovlen', _msg_iovlen_t),
92 ('msg_iovlen', _msg_iovlen_t),
93 ('msg_control', ctypes.c_void_p),
93 ('msg_control', ctypes.c_void_p),
94 ('msg_controllen', _msg_controllen_t),
94 ('msg_controllen', _msg_controllen_t),
95 ('msg_flags', ctypes.c_int),
95 ('msg_flags', ctypes.c_int),
96 ]
96 ]
97
97
98 class _cmsghdr(ctypes.Structure):
98 class _cmsghdr(ctypes.Structure):
99 _fields_ = [
99 _fields_ = [
100 ('cmsg_len', _cmsg_len_t),
100 ('cmsg_len', _cmsg_len_t),
101 ('cmsg_level', ctypes.c_int),
101 ('cmsg_level', ctypes.c_int),
102 ('cmsg_type', ctypes.c_int),
102 ('cmsg_type', ctypes.c_int),
103 ('cmsg_data', ctypes.c_ubyte * 0),
103 ('cmsg_data', ctypes.c_ubyte * 0),
104 ]
104 ]
105
105
106 _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
106 _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
107 _recvmsg = _libc.recvmsg
107 _recvmsg = _libc.recvmsg
108 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
108 _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
109 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), ctypes.c_int)
109 _recvmsg.argtypes = (ctypes.c_int, ctypes.POINTER(_msghdr), ctypes.c_int)
110
110
111 def _CMSG_FIRSTHDR(msgh):
111 def _CMSG_FIRSTHDR(msgh):
112 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
112 if msgh.msg_controllen < ctypes.sizeof(_cmsghdr):
113 return
113 return
114 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
114 cmsgptr = ctypes.cast(msgh.msg_control, ctypes.POINTER(_cmsghdr))
115 return cmsgptr.contents
115 return cmsgptr.contents
116
116
117 # The pure version is less portable than the native version because the
117 # The pure version is less portable than the native version because the
118 # handling of socket ancillary data heavily depends on C preprocessor.
118 # handling of socket ancillary data heavily depends on C preprocessor.
119 # Also, some length fields are wrongly typed in Linux kernel.
119 # Also, some length fields are wrongly typed in Linux kernel.
120 def recvfds(sockfd):
120 def recvfds(sockfd):
121 """receive list of file descriptors via socket"""
121 """receive list of file descriptors via socket"""
122 dummy = (ctypes.c_ubyte * 1)()
122 dummy = (ctypes.c_ubyte * 1)()
123 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
123 iov = _iovec(ctypes.cast(dummy, ctypes.c_void_p), ctypes.sizeof(dummy))
124 cbuf = ctypes.create_string_buffer(256)
124 cbuf = ctypes.create_string_buffer(256)
125 msgh = _msghdr(None, 0,
125 msgh = _msghdr(None, 0,
126 ctypes.pointer(iov), 1,
126 ctypes.pointer(iov), 1,
127 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
127 ctypes.cast(cbuf, ctypes.c_void_p), ctypes.sizeof(cbuf),
128 0)
128 0)
129 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
129 r = _recvmsg(sockfd, ctypes.byref(msgh), 0)
130 if r < 0:
130 if r < 0:
131 e = ctypes.get_errno()
131 e = ctypes.get_errno()
132 raise OSError(e, os.strerror(e))
132 raise OSError(e, os.strerror(e))
133 # assumes that the first cmsg has fds because it isn't easy to write
133 # assumes that the first cmsg has fds because it isn't easy to write
134 # portable CMSG_NXTHDR() with ctypes.
134 # portable CMSG_NXTHDR() with ctypes.
135 cmsg = _CMSG_FIRSTHDR(msgh)
135 cmsg = _CMSG_FIRSTHDR(msgh)
136 if not cmsg:
136 if not cmsg:
137 return []
137 return []
138 if (cmsg.cmsg_level != socket.SOL_SOCKET or
138 if (cmsg.cmsg_level != socket.SOL_SOCKET or
139 cmsg.cmsg_type != _SCM_RIGHTS):
139 cmsg.cmsg_type != _SCM_RIGHTS):
140 return []
140 return []
141 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
141 rfds = ctypes.cast(cmsg.cmsg_data, ctypes.POINTER(ctypes.c_int))
142 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
142 rfdscount = ((cmsg.cmsg_len - _cmsghdr.cmsg_data.offset) /
143 ctypes.sizeof(ctypes.c_int))
143 ctypes.sizeof(ctypes.c_int))
144 return [rfds[i] for i in xrange(rfdscount)]
144 return [rfds[i] for i in xrange(rfdscount)]
145
145
146 else:
146 else:
147 import msvcrt
147 import msvcrt
148
148
149 _kernel32 = ctypes.windll.kernel32
149 _kernel32 = ctypes.windll.kernel32
150
150
151 _DWORD = ctypes.c_ulong
151 _DWORD = ctypes.c_ulong
152 _LPCSTR = _LPSTR = ctypes.c_char_p
152 _LPCSTR = _LPSTR = ctypes.c_char_p
153 _HANDLE = ctypes.c_void_p
153 _HANDLE = ctypes.c_void_p
154
154
155 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
155 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
156
156
157 # CreateFile
157 # CreateFile
158 _FILE_SHARE_READ = 0x00000001
158 _FILE_SHARE_READ = 0x00000001
159 _FILE_SHARE_WRITE = 0x00000002
159 _FILE_SHARE_WRITE = 0x00000002
160 _FILE_SHARE_DELETE = 0x00000004
160 _FILE_SHARE_DELETE = 0x00000004
161
161
162 _CREATE_ALWAYS = 2
162 _CREATE_ALWAYS = 2
163 _OPEN_EXISTING = 3
163 _OPEN_EXISTING = 3
164 _OPEN_ALWAYS = 4
164 _OPEN_ALWAYS = 4
165
165
166 _GENERIC_READ = 0x80000000
166 _GENERIC_READ = 0x80000000
167 _GENERIC_WRITE = 0x40000000
167 _GENERIC_WRITE = 0x40000000
168
168
169 _FILE_ATTRIBUTE_NORMAL = 0x80
169 _FILE_ATTRIBUTE_NORMAL = 0x80
170
170
171 # open_osfhandle flags
171 # open_osfhandle flags
172 _O_RDONLY = 0x0000
172 _O_RDONLY = 0x0000
173 _O_RDWR = 0x0002
173 _O_RDWR = 0x0002
174 _O_APPEND = 0x0008
174 _O_APPEND = 0x0008
175
175
176 _O_TEXT = 0x4000
176 _O_TEXT = 0x4000
177 _O_BINARY = 0x8000
177 _O_BINARY = 0x8000
178
178
179 # types of parameters of C functions used (required by pypy)
179 # types of parameters of C functions used (required by pypy)
180
180
181 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
181 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
182 _DWORD, _DWORD, _HANDLE]
182 _DWORD, _DWORD, _HANDLE]
183 _kernel32.CreateFileA.restype = _HANDLE
183 _kernel32.CreateFileA.restype = _HANDLE
184
184
185 def _raiseioerror(name):
185 def _raiseioerror(name):
186 err = ctypes.WinError()
186 err = ctypes.WinError()
187 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
187 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
188
188
189 class posixfile(object):
189 class posixfile(object):
190 '''a file object aiming for POSIX-like semantics
190 '''a file object aiming for POSIX-like semantics
191
191
192 CPython's open() returns a file that was opened *without* setting the
192 CPython's open() returns a file that was opened *without* setting the
193 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
193 _FILE_SHARE_DELETE flag, which causes rename and unlink to abort.
194 This even happens if any hardlinked copy of the file is in open state.
194 This even happens if any hardlinked copy of the file is in open state.
195 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
195 We set _FILE_SHARE_DELETE here, so files opened with posixfile can be
196 renamed and deleted while they are held open.
196 renamed and deleted while they are held open.
197 Note that if a file opened with posixfile is unlinked, the file
197 Note that if a file opened with posixfile is unlinked, the file
198 remains but cannot be opened again or be recreated under the same name,
198 remains but cannot be opened again or be recreated under the same name,
199 until all reading processes have closed the file.'''
199 until all reading processes have closed the file.'''
200
200
201 def __init__(self, name, mode='r', bufsize=-1):
201 def __init__(self, name, mode='r', bufsize=-1):
202 if 'b' in mode:
202 if 'b' in mode:
203 flags = _O_BINARY
203 flags = _O_BINARY
204 else:
204 else:
205 flags = _O_TEXT
205 flags = _O_TEXT
206
206
207 m0 = mode[0]
207 m0 = mode[0]
208 if m0 == 'r' and '+' not in mode:
208 if m0 == 'r' and '+' not in mode:
209 flags |= _O_RDONLY
209 flags |= _O_RDONLY
210 access = _GENERIC_READ
210 access = _GENERIC_READ
211 else:
211 else:
212 # work around http://support.microsoft.com/kb/899149 and
212 # work around http://support.microsoft.com/kb/899149 and
213 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
213 # set _O_RDWR for 'w' and 'a', even if mode has no '+'
214 flags |= _O_RDWR
214 flags |= _O_RDWR
215 access = _GENERIC_READ | _GENERIC_WRITE
215 access = _GENERIC_READ | _GENERIC_WRITE
216
216
217 if m0 == 'r':
217 if m0 == 'r':
218 creation = _OPEN_EXISTING
218 creation = _OPEN_EXISTING
219 elif m0 == 'w':
219 elif m0 == 'w':
220 creation = _CREATE_ALWAYS
220 creation = _CREATE_ALWAYS
221 elif m0 == 'a':
221 elif m0 == 'a':
222 creation = _OPEN_ALWAYS
222 creation = _OPEN_ALWAYS
223 flags |= _O_APPEND
223 flags |= _O_APPEND
224 else:
224 else:
225 raise ValueError("invalid mode: %s" % mode)
225 raise ValueError("invalid mode: %s" % mode)
226
226
227 fh = _kernel32.CreateFileA(name, access,
227 fh = _kernel32.CreateFileA(name, access,
228 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
228 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
229 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
229 None, creation, _FILE_ATTRIBUTE_NORMAL, None)
230 if fh == _INVALID_HANDLE_VALUE:
230 if fh == _INVALID_HANDLE_VALUE:
231 _raiseioerror(name)
231 _raiseioerror(name)
232
232
233 fd = msvcrt.open_osfhandle(fh, flags)
233 fd = msvcrt.open_osfhandle(fh, flags)
234 if fd == -1:
234 if fd == -1:
235 _kernel32.CloseHandle(fh)
235 _kernel32.CloseHandle(fh)
236 _raiseioerror(name)
236 _raiseioerror(name)
237
237
238 f = os.fdopen(fd, mode, bufsize)
238 f = os.fdopen(fd, mode, bufsize)
239 # unfortunately, f.name is '<fdopen>' at this point -- so we store
239 # unfortunately, f.name is '<fdopen>' at this point -- so we store
240 # the name on this wrapper. We cannot just assign to f.name,
240 # the name on this wrapper. We cannot just assign to f.name,
241 # because that attribute is read-only.
241 # because that attribute is read-only.
242 object.__setattr__(self, 'name', name)
242 object.__setattr__(self, 'name', name)
243 object.__setattr__(self, '_file', f)
243 object.__setattr__(self, '_file', f)
244
244
245 def __iter__(self):
245 def __iter__(self):
246 return self._file
246 return self._file
247
247
248 def __getattr__(self, name):
248 def __getattr__(self, name):
249 return getattr(self._file, name)
249 return getattr(self._file, name)
250
250
251 def __setattr__(self, name, value):
251 def __setattr__(self, name, value):
252 '''mimics the read-only attributes of Python file objects
252 '''mimics the read-only attributes of Python file objects
253 by raising 'TypeError: readonly attribute' if someone tries:
253 by raising 'TypeError: readonly attribute' if someone tries:
254 f = posixfile('foo.txt')
254 f = posixfile('foo.txt')
255 f.name = 'bla' '''
255 f.name = 'bla' '''
256 return self._file.__setattr__(name, value)
256 return self._file.__setattr__(name, value)
257
258 def __enter__(self):
259 return self._file.__enter__()
260
261 def __exit__(self, exc_type, exc_value, exc_tb):
262 return self._file.__exit__(exc_type, exc_value, exc_tb)
General Comments 0
You need to be logged in to leave comments. Login now