##// END OF EJS Templates
win32: allow catching of both pywintypes.error and WindowsError
Bryan O'Sullivan -
r8328:91f1fe78 default
parent child Browse files
Show More
@@ -1,380 +1,386
1 1 # win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005-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, incorporated herein by reference.
7 7
8 8 """Utility functions that use win32 API.
9 9
10 10 Mark Hammond's win32all package allows better functionality on
11 11 Windows. This module overrides definitions in util.py. If not
12 12 available, import of this module will fail, and generic code will be
13 13 used.
14 14 """
15 15
16 16 import win32api
17 17
18 18 import errno, os, sys, pywintypes, win32con, win32file, win32process
19 19 import cStringIO, winerror
20 20 import osutil, encoding
21 21 import util
22 22 from win32com.shell import shell,shellcon
23 23
24 24 class WinError(Exception):
25 25 winerror_map = {
26 26 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
27 27 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
28 28 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
29 29 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
30 30 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
31 31 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
32 32 winerror.ERROR_BAD_COMMAND: errno.EIO,
33 33 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
34 34 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
35 35 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
36 36 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
37 37 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
38 38 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
39 39 winerror.ERROR_BAD_PIPE: errno.EPIPE,
40 40 winerror.ERROR_BAD_UNIT: errno.ENODEV,
41 41 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
42 42 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
43 43 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
44 44 winerror.ERROR_BUSY: errno.EBUSY,
45 45 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
46 46 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
47 47 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
48 48 winerror.ERROR_CANTOPEN: errno.EIO,
49 49 winerror.ERROR_CANTREAD: errno.EIO,
50 50 winerror.ERROR_CANTWRITE: errno.EIO,
51 51 winerror.ERROR_CRC: errno.EIO,
52 52 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
53 53 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
54 54 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
55 55 winerror.ERROR_DIRECTORY: errno.EINVAL,
56 56 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
57 57 winerror.ERROR_DISK_CHANGE: errno.EIO,
58 58 winerror.ERROR_DISK_FULL: errno.ENOSPC,
59 59 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
60 60 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
61 61 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
62 62 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
63 63 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
64 64 winerror.ERROR_FILE_INVALID: errno.ENODEV,
65 65 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
66 66 winerror.ERROR_GEN_FAILURE: errno.EIO,
67 67 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
68 68 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
69 69 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
70 70 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
71 71 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
72 72 winerror.ERROR_INVALID_DATA: errno.EINVAL,
73 73 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
74 74 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
75 75 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
76 76 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
77 77 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
78 78 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
79 79 winerror.ERROR_INVALID_NAME: errno.EINVAL,
80 80 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
81 81 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
82 82 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
83 83 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
84 84 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
85 85 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
86 86 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
87 87 winerror.ERROR_IO_DEVICE: errno.EIO,
88 88 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
89 89 winerror.ERROR_LOCKED: errno.EBUSY,
90 90 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
91 91 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
92 92 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
93 93 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
94 94 winerror.ERROR_MORE_DATA: errno.EPIPE,
95 95 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
96 96 winerror.ERROR_NOACCESS: errno.EFAULT,
97 97 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
98 98 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
99 99 winerror.ERROR_NOT_READY: errno.EAGAIN,
100 100 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
101 101 winerror.ERROR_NO_DATA: errno.EPIPE,
102 102 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
103 103 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
104 104 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
105 105 winerror.ERROR_OPEN_FAILED: errno.EIO,
106 106 winerror.ERROR_OPEN_FILES: errno.EBUSY,
107 107 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
108 108 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
109 109 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
110 110 winerror.ERROR_PATH_BUSY: errno.EBUSY,
111 111 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
112 112 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
113 113 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
114 114 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
115 115 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
116 116 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
117 117 winerror.ERROR_READ_FAULT: errno.EIO,
118 118 winerror.ERROR_SEEK: errno.EIO,
119 119 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
120 120 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
121 121 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
122 122 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
123 123 winerror.ERROR_SWAPERROR: errno.ENOENT,
124 124 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
125 125 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
126 126 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
127 127 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
128 128 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
129 129 winerror.ERROR_WRITE_FAULT: errno.EIO,
130 130 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
131 131 }
132 132
133 133 def __init__(self, err):
134 self.win_errno, self.win_function, self.win_strerror = err
135 if self.win_strerror.endswith('.'):
136 self.win_strerror = self.win_strerror[:-1]
134 try:
135 # unpack a pywintypes.error tuple
136 self.win_errno, self.win_function, self.win_strerror = err
137 except ValueError:
138 # get attributes from a WindowsError
139 self.win_errno = err.winerror
140 self.win_function = None
141 self.win_strerror = err.strerror
142 self.win_strerror = self.win_strerror.rstrip('.')
137 143
138 144 class WinIOError(WinError, IOError):
139 145 def __init__(self, err, filename=None):
140 146 WinError.__init__(self, err)
141 147 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
142 148 self.win_strerror)
143 149 self.filename = filename
144 150
145 151 class WinOSError(WinError, OSError):
146 152 def __init__(self, err):
147 153 WinError.__init__(self, err)
148 154 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
149 155 self.win_strerror)
150 156
151 157 def os_link(src, dst):
152 158 try:
153 159 win32file.CreateHardLink(dst, src)
154 160 # CreateHardLink sometimes succeeds on mapped drives but
155 161 # following nlinks() returns 1. Check it now and bail out.
156 162 if nlinks(src) < 2:
157 163 try:
158 164 win32file.DeleteFile(dst)
159 165 except:
160 166 pass
161 167 # Fake hardlinking error
162 168 raise WinOSError((18, 'CreateHardLink', 'The system cannot '
163 169 'move the file to a different disk drive'))
164 170 except pywintypes.error, details:
165 171 raise WinOSError(details)
166 172 except NotImplementedError: # Another fake error win Win98
167 173 raise WinOSError((18, 'CreateHardLink', 'Hardlinking not supported'))
168 174
169 175 def nlinks(pathname):
170 176 """Return number of hardlinks for the given file."""
171 177 try:
172 178 fh = win32file.CreateFile(pathname,
173 179 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
174 180 None, win32file.OPEN_EXISTING, 0, None)
175 181 res = win32file.GetFileInformationByHandle(fh)
176 182 fh.Close()
177 183 return res[7]
178 184 except pywintypes.error:
179 185 return os.lstat(pathname).st_nlink
180 186
181 187 def testpid(pid):
182 188 '''return True if pid is still running or unable to
183 189 determine, False otherwise'''
184 190 try:
185 191 handle = win32api.OpenProcess(
186 192 win32con.PROCESS_QUERY_INFORMATION, False, pid)
187 193 if handle:
188 194 status = win32process.GetExitCodeProcess(handle)
189 195 return status == win32con.STILL_ACTIVE
190 196 except pywintypes.error, details:
191 197 return details[0] != winerror.ERROR_INVALID_PARAMETER
192 198 return True
193 199
194 200 def lookup_reg(key, valname=None, scope=None):
195 201 ''' Look up a key/value name in the Windows registry.
196 202
197 203 valname: value name. If unspecified, the default value for the key
198 204 is used.
199 205 scope: optionally specify scope for registry lookup, this can be
200 206 a sequence of scopes to look up in order. Default (CURRENT_USER,
201 207 LOCAL_MACHINE).
202 208 '''
203 209 try:
204 210 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
205 211 QueryValueEx, OpenKey
206 212 except ImportError:
207 213 return None
208 214
209 215 if scope is None:
210 216 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
211 217 elif not isinstance(scope, (list, tuple)):
212 218 scope = (scope,)
213 219 for s in scope:
214 220 try:
215 221 val = QueryValueEx(OpenKey(s, key), valname)[0]
216 222 # never let a Unicode string escape into the wild
217 223 return encoding.tolocal(val.encode('UTF-8'))
218 224 except EnvironmentError:
219 225 pass
220 226
221 227 def system_rcpath_win32():
222 228 '''return default os-specific hgrc search path'''
223 229 proc = win32api.GetCurrentProcess()
224 230 try:
225 231 # This will fail on windows < NT
226 232 filename = win32process.GetModuleFileNameEx(proc, 0)
227 233 except:
228 234 filename = win32api.GetModuleFileName(0)
229 235 # Use mercurial.ini found in directory with hg.exe
230 236 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
231 237 if os.path.isfile(progrc):
232 238 return [progrc]
233 239 # else look for a system rcpath in the registry
234 240 try:
235 241 value = win32api.RegQueryValue(
236 242 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
237 243 rcpath = []
238 244 for p in value.split(os.pathsep):
239 245 if p.lower().endswith('mercurial.ini'):
240 246 rcpath.append(p)
241 247 elif os.path.isdir(p):
242 248 for f, kind in osutil.listdir(p):
243 249 if f.endswith('.rc'):
244 250 rcpath.append(os.path.join(p, f))
245 251 return rcpath
246 252 except pywintypes.error:
247 253 return []
248 254
249 255 def user_rcpath_win32():
250 256 '''return os-specific hgrc search path to the user dir'''
251 257 userdir = os.path.expanduser('~')
252 258 if sys.getwindowsversion()[3] != 2 and userdir == '~':
253 259 # We are on win < nt: fetch the APPDATA directory location and use
254 260 # the parent directory as the user home dir.
255 261 appdir = shell.SHGetPathFromIDList(
256 262 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
257 263 userdir = os.path.dirname(appdir)
258 264 return [os.path.join(userdir, 'mercurial.ini'),
259 265 os.path.join(userdir, '.hgrc')]
260 266
261 267 class posixfile_nt(object):
262 268 '''file object with posix-like semantics. on windows, normal
263 269 files can not be deleted or renamed if they are open. must open
264 270 with win32file.FILE_SHARE_DELETE. this flag does not exist on
265 271 windows < nt, so do not use this class there.'''
266 272
267 273 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
268 274 # but does not work at all. wrap win32 file api instead.
269 275
270 276 def __init__(self, name, mode='rb'):
271 277 self.closed = False
272 278 self.name = name
273 279 self.mode = mode
274 280 access = 0
275 281 if 'r' in mode or '+' in mode:
276 282 access |= win32file.GENERIC_READ
277 283 if 'w' in mode or 'a' in mode or '+' in mode:
278 284 access |= win32file.GENERIC_WRITE
279 285 if 'r' in mode:
280 286 creation = win32file.OPEN_EXISTING
281 287 elif 'a' in mode:
282 288 creation = win32file.OPEN_ALWAYS
283 289 else:
284 290 creation = win32file.CREATE_ALWAYS
285 291 try:
286 292 self.handle = win32file.CreateFile(name,
287 293 access,
288 294 win32file.FILE_SHARE_READ |
289 295 win32file.FILE_SHARE_WRITE |
290 296 win32file.FILE_SHARE_DELETE,
291 297 None,
292 298 creation,
293 299 win32file.FILE_ATTRIBUTE_NORMAL,
294 300 0)
295 301 except pywintypes.error, err:
296 302 raise WinIOError(err, name)
297 303
298 304 def __iter__(self):
299 305 for line in self.readlines():
300 306 yield line
301 307
302 308 def read(self, count=-1):
303 309 try:
304 310 cs = cStringIO.StringIO()
305 311 while count:
306 312 wincount = int(count)
307 313 if wincount == -1:
308 314 wincount = 1048576
309 315 val, data = win32file.ReadFile(self.handle, wincount)
310 316 if not data: break
311 317 cs.write(data)
312 318 if count != -1:
313 319 count -= len(data)
314 320 return cs.getvalue()
315 321 except pywintypes.error, err:
316 322 raise WinIOError(err)
317 323
318 324 def readlines(self, sizehint=None):
319 325 # splitlines() splits on single '\r' while readlines()
320 326 # does not. cStringIO has a well behaving readlines() and is fast.
321 327 return cStringIO.StringIO(self.read()).readlines()
322 328
323 329 def write(self, data):
324 330 try:
325 331 if 'a' in self.mode:
326 332 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
327 333 nwrit = 0
328 334 while nwrit < len(data):
329 335 val, nwrit = win32file.WriteFile(self.handle, data)
330 336 data = data[nwrit:]
331 337 except pywintypes.error, err:
332 338 raise WinIOError(err)
333 339
334 340 def writelines(self, sequence):
335 341 for s in sequence:
336 342 self.write(s)
337 343
338 344 def seek(self, pos, whence=0):
339 345 try:
340 346 win32file.SetFilePointer(self.handle, int(pos), whence)
341 347 except pywintypes.error, err:
342 348 raise WinIOError(err)
343 349
344 350 def tell(self):
345 351 try:
346 352 return win32file.SetFilePointer(self.handle, 0,
347 353 win32file.FILE_CURRENT)
348 354 except pywintypes.error, err:
349 355 raise WinIOError(err)
350 356
351 357 def close(self):
352 358 if not self.closed:
353 359 self.handle = None
354 360 self.closed = True
355 361
356 362 def flush(self):
357 363 # we have no application-level buffering
358 364 pass
359 365
360 366 def truncate(self, pos=0):
361 367 try:
362 368 win32file.SetFilePointer(self.handle, int(pos),
363 369 win32file.FILE_BEGIN)
364 370 win32file.SetEndOfFile(self.handle)
365 371 except pywintypes.error, err:
366 372 raise WinIOError(err)
367 373
368 374 def getuser():
369 375 '''return name of current user'''
370 376 return win32api.GetUserName()
371 377
372 378 def set_signal_handler_win32():
373 379 """Register a termination handler for console events including
374 380 CTRL+C. python signal handlers do not work well with socket
375 381 operations.
376 382 """
377 383 def handler(event):
378 384 win32process.ExitProcess(1)
379 385 win32api.SetConsoleCtrlHandler(handler)
380 386
General Comments 0
You need to be logged in to leave comments. Login now