##// END OF EJS Templates
windows: do not replace sys.stdout by winstdout...
Yuya Nishihara -
r30474:b02e210a default
parent child Browse files
Show More
@@ -1,480 +1,478 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
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 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import msvcrt
12 12 import os
13 13 import re
14 14 import stat
15 15 import sys
16 16
17 17 from .i18n import _
18 18 from . import (
19 19 encoding,
20 20 osutil,
21 21 win32,
22 22 )
23 23
24 24 try:
25 25 import _winreg as winreg
26 26 winreg.CloseKey
27 27 except ImportError:
28 28 import winreg
29 29
30 30 executablepath = win32.executablepath
31 31 getuser = win32.getuser
32 32 hidewindow = win32.hidewindow
33 33 makedir = win32.makedir
34 34 nlinks = win32.nlinks
35 35 oslink = win32.oslink
36 36 samedevice = win32.samedevice
37 37 samefile = win32.samefile
38 38 setsignalhandler = win32.setsignalhandler
39 39 spawndetached = win32.spawndetached
40 40 split = os.path.split
41 41 testpid = win32.testpid
42 42 unlink = win32.unlink
43 43
44 44 umask = 0o022
45 45
46 46 class mixedfilemodewrapper(object):
47 47 """Wraps a file handle when it is opened in read/write mode.
48 48
49 49 fopen() and fdopen() on Windows have a specific-to-Windows requirement
50 50 that files opened with mode r+, w+, or a+ make a call to a file positioning
51 51 function when switching between reads and writes. Without this extra call,
52 52 Python will raise a not very intuitive "IOError: [Errno 0] Error."
53 53
54 54 This class wraps posixfile instances when the file is opened in read/write
55 55 mode and automatically adds checks or inserts appropriate file positioning
56 56 calls when necessary.
57 57 """
58 58 OPNONE = 0
59 59 OPREAD = 1
60 60 OPWRITE = 2
61 61
62 62 def __init__(self, fp):
63 63 object.__setattr__(self, '_fp', fp)
64 64 object.__setattr__(self, '_lastop', 0)
65 65
66 66 def __getattr__(self, name):
67 67 return getattr(self._fp, name)
68 68
69 69 def __setattr__(self, name, value):
70 70 return self._fp.__setattr__(name, value)
71 71
72 72 def _noopseek(self):
73 73 self._fp.seek(0, os.SEEK_CUR)
74 74
75 75 def seek(self, *args, **kwargs):
76 76 object.__setattr__(self, '_lastop', self.OPNONE)
77 77 return self._fp.seek(*args, **kwargs)
78 78
79 79 def write(self, d):
80 80 if self._lastop == self.OPREAD:
81 81 self._noopseek()
82 82
83 83 object.__setattr__(self, '_lastop', self.OPWRITE)
84 84 return self._fp.write(d)
85 85
86 86 def writelines(self, *args, **kwargs):
87 87 if self._lastop == self.OPREAD:
88 88 self._noopeseek()
89 89
90 90 object.__setattr__(self, '_lastop', self.OPWRITE)
91 91 return self._fp.writelines(*args, **kwargs)
92 92
93 93 def read(self, *args, **kwargs):
94 94 if self._lastop == self.OPWRITE:
95 95 self._noopseek()
96 96
97 97 object.__setattr__(self, '_lastop', self.OPREAD)
98 98 return self._fp.read(*args, **kwargs)
99 99
100 100 def readline(self, *args, **kwargs):
101 101 if self._lastop == self.OPWRITE:
102 102 self._noopseek()
103 103
104 104 object.__setattr__(self, '_lastop', self.OPREAD)
105 105 return self._fp.readline(*args, **kwargs)
106 106
107 107 def readlines(self, *args, **kwargs):
108 108 if self._lastop == self.OPWRITE:
109 109 self._noopseek()
110 110
111 111 object.__setattr__(self, '_lastop', self.OPREAD)
112 112 return self._fp.readlines(*args, **kwargs)
113 113
114 114 def posixfile(name, mode='r', buffering=-1):
115 115 '''Open a file with even more POSIX-like semantics'''
116 116 try:
117 117 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
118 118
119 119 # The position when opening in append mode is implementation defined, so
120 120 # make it consistent with other platforms, which position at EOF.
121 121 if 'a' in mode:
122 122 fp.seek(0, os.SEEK_END)
123 123
124 124 if '+' in mode:
125 125 return mixedfilemodewrapper(fp)
126 126
127 127 return fp
128 128 except WindowsError as err:
129 129 # convert to a friendlier exception
130 130 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
131 131
132 132 class winstdout(object):
133 133 '''stdout on windows misbehaves if sent through a pipe'''
134 134
135 135 def __init__(self, fp):
136 136 self.fp = fp
137 137
138 138 def __getattr__(self, key):
139 139 return getattr(self.fp, key)
140 140
141 141 def close(self):
142 142 try:
143 143 self.fp.close()
144 144 except IOError:
145 145 pass
146 146
147 147 def write(self, s):
148 148 try:
149 149 # This is workaround for "Not enough space" error on
150 150 # writing large size of data to console.
151 151 limit = 16000
152 152 l = len(s)
153 153 start = 0
154 154 self.softspace = 0
155 155 while start < l:
156 156 end = start + limit
157 157 self.fp.write(s[start:end])
158 158 start = end
159 159 except IOError as inst:
160 160 if inst.errno != 0:
161 161 raise
162 162 self.close()
163 163 raise IOError(errno.EPIPE, 'Broken pipe')
164 164
165 165 def flush(self):
166 166 try:
167 167 return self.fp.flush()
168 168 except IOError as inst:
169 169 if inst.errno != errno.EINVAL:
170 170 raise
171 171 self.close()
172 172 raise IOError(errno.EPIPE, 'Broken pipe')
173 173
174 sys.stdout = winstdout(sys.stdout)
175
176 174 def _is_win_9x():
177 175 '''return true if run on windows 95, 98 or me.'''
178 176 try:
179 177 return sys.getwindowsversion()[3] == 1
180 178 except AttributeError:
181 179 return 'command' in os.environ.get('comspec', '')
182 180
183 181 def openhardlinks():
184 182 return not _is_win_9x()
185 183
186 184 def parsepatchoutput(output_line):
187 185 """parses the output produced by patch and returns the filename"""
188 186 pf = output_line[14:]
189 187 if pf[0] == '`':
190 188 pf = pf[1:-1] # Remove the quotes
191 189 return pf
192 190
193 191 def sshargs(sshcmd, host, user, port):
194 192 '''Build argument list for ssh or Plink'''
195 193 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
196 194 args = user and ("%s@%s" % (user, host)) or host
197 195 return port and ("%s %s %s" % (args, pflag, port)) or args
198 196
199 197 def setflags(f, l, x):
200 198 pass
201 199
202 200 def copymode(src, dst, mode=None):
203 201 pass
204 202
205 203 def checkexec(path):
206 204 return False
207 205
208 206 def checklink(path):
209 207 return False
210 208
211 209 def setbinary(fd):
212 210 # When run without console, pipes may expose invalid
213 211 # fileno(), usually set to -1.
214 212 fno = getattr(fd, 'fileno', None)
215 213 if fno is not None and fno() >= 0:
216 214 msvcrt.setmode(fno(), os.O_BINARY)
217 215
218 216 def pconvert(path):
219 217 return path.replace(os.sep, '/')
220 218
221 219 def localpath(path):
222 220 return path.replace('/', '\\')
223 221
224 222 def normpath(path):
225 223 return pconvert(os.path.normpath(path))
226 224
227 225 def normcase(path):
228 226 return encoding.upper(path) # NTFS compares via upper()
229 227
230 228 # see posix.py for definitions
231 229 normcasespec = encoding.normcasespecs.upper
232 230 normcasefallback = encoding.upperfallback
233 231
234 232 def samestat(s1, s2):
235 233 return False
236 234
237 235 # A sequence of backslashes is special iff it precedes a double quote:
238 236 # - if there's an even number of backslashes, the double quote is not
239 237 # quoted (i.e. it ends the quoted region)
240 238 # - if there's an odd number of backslashes, the double quote is quoted
241 239 # - in both cases, every pair of backslashes is unquoted into a single
242 240 # backslash
243 241 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
244 242 # So, to quote a string, we must surround it in double quotes, double
245 243 # the number of backslashes that precede double quotes and add another
246 244 # backslash before every double quote (being careful with the double
247 245 # quote we've appended to the end)
248 246 _quotere = None
249 247 _needsshellquote = None
250 248 def shellquote(s):
251 249 r"""
252 250 >>> shellquote(r'C:\Users\xyz')
253 251 '"C:\\Users\\xyz"'
254 252 >>> shellquote(r'C:\Users\xyz/mixed')
255 253 '"C:\\Users\\xyz/mixed"'
256 254 >>> # Would be safe not to quote too, since it is all double backslashes
257 255 >>> shellquote(r'C:\\Users\\xyz')
258 256 '"C:\\\\Users\\\\xyz"'
259 257 >>> # But this must be quoted
260 258 >>> shellquote(r'C:\\Users\\xyz/abc')
261 259 '"C:\\\\Users\\\\xyz/abc"'
262 260 """
263 261 global _quotere
264 262 if _quotere is None:
265 263 _quotere = re.compile(r'(\\*)("|\\$)')
266 264 global _needsshellquote
267 265 if _needsshellquote is None:
268 266 # ":" is also treated as "safe character", because it is used as a part
269 267 # of path name on Windows. "\" is also part of a path name, but isn't
270 268 # safe because shlex.split() (kind of) treats it as an escape char and
271 269 # drops it. It will leave the next character, even if it is another
272 270 # "\".
273 271 _needsshellquote = re.compile(r'[^a-zA-Z0-9._:/-]').search
274 272 if s and not _needsshellquote(s) and not _quotere.search(s):
275 273 # "s" shouldn't have to be quoted
276 274 return s
277 275 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
278 276
279 277 def quotecommand(cmd):
280 278 """Build a command string suitable for os.popen* calls."""
281 279 if sys.version_info < (2, 7, 1):
282 280 # Python versions since 2.7.1 do this extra quoting themselves
283 281 return '"' + cmd + '"'
284 282 return cmd
285 283
286 284 def popen(command, mode='r'):
287 285 # Work around "popen spawned process may not write to stdout
288 286 # under windows"
289 287 # http://bugs.python.org/issue1366
290 288 command += " 2> %s" % os.devnull
291 289 return os.popen(quotecommand(command), mode)
292 290
293 291 def explainexit(code):
294 292 return _("exited with status %d") % code, code
295 293
296 294 # if you change this stub into a real check, please try to implement the
297 295 # username and groupname functions above, too.
298 296 def isowner(st):
299 297 return True
300 298
301 299 def findexe(command):
302 300 '''Find executable for command searching like cmd.exe does.
303 301 If command is a basename then PATH is searched for command.
304 302 PATH isn't searched if command is an absolute or relative path.
305 303 An extension from PATHEXT is found and added if not present.
306 304 If command isn't found None is returned.'''
307 305 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
308 306 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
309 307 if os.path.splitext(command)[1].lower() in pathexts:
310 308 pathexts = ['']
311 309
312 310 def findexisting(pathcommand):
313 311 'Will append extension (if needed) and return existing file'
314 312 for ext in pathexts:
315 313 executable = pathcommand + ext
316 314 if os.path.exists(executable):
317 315 return executable
318 316 return None
319 317
320 318 if os.sep in command:
321 319 return findexisting(command)
322 320
323 321 for path in os.environ.get('PATH', '').split(os.pathsep):
324 322 executable = findexisting(os.path.join(path, command))
325 323 if executable is not None:
326 324 return executable
327 325 return findexisting(os.path.expanduser(os.path.expandvars(command)))
328 326
329 327 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
330 328
331 329 def statfiles(files):
332 330 '''Stat each file in files. Yield each stat, or None if a file
333 331 does not exist or has a type we don't care about.
334 332
335 333 Cluster and cache stat per directory to minimize number of OS stat calls.'''
336 334 dircache = {} # dirname -> filename -> status | None if file does not exist
337 335 getkind = stat.S_IFMT
338 336 for nf in files:
339 337 nf = normcase(nf)
340 338 dir, base = os.path.split(nf)
341 339 if not dir:
342 340 dir = '.'
343 341 cache = dircache.get(dir, None)
344 342 if cache is None:
345 343 try:
346 344 dmap = dict([(normcase(n), s)
347 345 for n, k, s in osutil.listdir(dir, True)
348 346 if getkind(s.st_mode) in _wantedkinds])
349 347 except OSError as err:
350 348 # Python >= 2.5 returns ENOENT and adds winerror field
351 349 # EINVAL is raised if dir is not a directory.
352 350 if err.errno not in (errno.ENOENT, errno.EINVAL,
353 351 errno.ENOTDIR):
354 352 raise
355 353 dmap = {}
356 354 cache = dircache.setdefault(dir, dmap)
357 355 yield cache.get(base, None)
358 356
359 357 def username(uid=None):
360 358 """Return the name of the user with the given uid.
361 359
362 360 If uid is None, return the name of the current user."""
363 361 return None
364 362
365 363 def groupname(gid=None):
366 364 """Return the name of the group with the given gid.
367 365
368 366 If gid is None, return the name of the current group."""
369 367 return None
370 368
371 369 def removedirs(name):
372 370 """special version of os.removedirs that does not remove symlinked
373 371 directories or junction points if they actually contain files"""
374 372 if osutil.listdir(name):
375 373 return
376 374 os.rmdir(name)
377 375 head, tail = os.path.split(name)
378 376 if not tail:
379 377 head, tail = os.path.split(head)
380 378 while head and tail:
381 379 try:
382 380 if osutil.listdir(head):
383 381 return
384 382 os.rmdir(head)
385 383 except (ValueError, OSError):
386 384 break
387 385 head, tail = os.path.split(head)
388 386
389 387 def unlinkpath(f, ignoremissing=False):
390 388 """unlink and remove the directory if it is empty"""
391 389 try:
392 390 unlink(f)
393 391 except OSError as e:
394 392 if not (ignoremissing and e.errno == errno.ENOENT):
395 393 raise
396 394 # try removing directories that might now be empty
397 395 try:
398 396 removedirs(os.path.dirname(f))
399 397 except OSError:
400 398 pass
401 399
402 400 def rename(src, dst):
403 401 '''atomically rename file src to dst, replacing dst if it exists'''
404 402 try:
405 403 os.rename(src, dst)
406 404 except OSError as e:
407 405 if e.errno != errno.EEXIST:
408 406 raise
409 407 unlink(dst)
410 408 os.rename(src, dst)
411 409
412 410 def gethgcmd():
413 411 return [sys.executable] + sys.argv[:1]
414 412
415 413 def groupmembers(name):
416 414 # Don't support groups on Windows for now
417 415 raise KeyError
418 416
419 417 def isexec(f):
420 418 return False
421 419
422 420 class cachestat(object):
423 421 def __init__(self, path):
424 422 pass
425 423
426 424 def cacheable(self):
427 425 return False
428 426
429 427 def lookupreg(key, valname=None, scope=None):
430 428 ''' Look up a key/value name in the Windows registry.
431 429
432 430 valname: value name. If unspecified, the default value for the key
433 431 is used.
434 432 scope: optionally specify scope for registry lookup, this can be
435 433 a sequence of scopes to look up in order. Default (CURRENT_USER,
436 434 LOCAL_MACHINE).
437 435 '''
438 436 if scope is None:
439 437 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
440 438 elif not isinstance(scope, (list, tuple)):
441 439 scope = (scope,)
442 440 for s in scope:
443 441 try:
444 442 val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0]
445 443 # never let a Unicode string escape into the wild
446 444 return encoding.tolocal(val.encode('UTF-8'))
447 445 except EnvironmentError:
448 446 pass
449 447
450 448 expandglobs = True
451 449
452 450 def statislink(st):
453 451 '''check whether a stat result is a symlink'''
454 452 return False
455 453
456 454 def statisexec(st):
457 455 '''check whether a stat result is an executable file'''
458 456 return False
459 457
460 458 def poll(fds):
461 459 # see posix.py for description
462 460 raise NotImplementedError()
463 461
464 462 def readpipe(pipe):
465 463 """Read all available data from a pipe."""
466 464 chunks = []
467 465 while True:
468 466 size = win32.peekpipe(pipe)
469 467 if not size:
470 468 break
471 469
472 470 s = pipe.read(size)
473 471 if not s:
474 472 break
475 473 chunks.append(s)
476 474
477 475 return ''.join(chunks)
478 476
479 477 def bindunixsocket(sock, path):
480 478 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now