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