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