##// END OF EJS Templates
windows: remove write throttling support...
Gregory Szorc -
r49754:563eb25e default
parent child Browse files
Show More
@@ -1,739 +1,728 b''
1 1 # windows.py - Windows utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Olivia Mackall <olivia@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
9 9 import errno
10 10 import getpass
11 11 import msvcrt
12 12 import os
13 13 import re
14 14 import stat
15 15 import string
16 16 import sys
17 17
18 18 from .i18n import _
19 19 from .pycompat import getattr
20 20 from . import (
21 21 encoding,
22 22 error,
23 23 policy,
24 24 pycompat,
25 25 win32,
26 26 )
27 27
28 28 try:
29 29 import _winreg as winreg # pytype: disable=import-error
30 30
31 31 winreg.CloseKey
32 32 except ImportError:
33 33 # py2 only
34 34 import winreg # pytype: disable=import-error
35 35
36 36 osutil = policy.importmod('osutil')
37 37
38 38 getfsmountpoint = win32.getvolumename
39 39 getfstype = win32.getfstype
40 40 getuser = win32.getuser
41 41 hidewindow = win32.hidewindow
42 42 makedir = win32.makedir
43 43 nlinks = win32.nlinks
44 44 oslink = win32.oslink
45 45 samedevice = win32.samedevice
46 46 samefile = win32.samefile
47 47 setsignalhandler = win32.setsignalhandler
48 48 spawndetached = win32.spawndetached
49 49 split = os.path.split
50 50 testpid = win32.testpid
51 51 unlink = win32.unlink
52 52
53 53 umask = 0o022
54 54
55 55
56 56 class mixedfilemodewrapper(object):
57 57 """Wraps a file handle when it is opened in read/write mode.
58 58
59 59 fopen() and fdopen() on Windows have a specific-to-Windows requirement
60 60 that files opened with mode r+, w+, or a+ make a call to a file positioning
61 61 function when switching between reads and writes. Without this extra call,
62 62 Python will raise a not very intuitive "IOError: [Errno 0] Error."
63 63
64 64 This class wraps posixfile instances when the file is opened in read/write
65 65 mode and automatically adds checks or inserts appropriate file positioning
66 66 calls when necessary.
67 67 """
68 68
69 69 OPNONE = 0
70 70 OPREAD = 1
71 71 OPWRITE = 2
72 72
73 73 def __init__(self, fp):
74 74 object.__setattr__(self, '_fp', fp)
75 75 object.__setattr__(self, '_lastop', 0)
76 76
77 77 def __enter__(self):
78 78 self._fp.__enter__()
79 79 return self
80 80
81 81 def __exit__(self, exc_type, exc_val, exc_tb):
82 82 self._fp.__exit__(exc_type, exc_val, exc_tb)
83 83
84 84 def __getattr__(self, name):
85 85 return getattr(self._fp, name)
86 86
87 87 def __setattr__(self, name, value):
88 88 return self._fp.__setattr__(name, value)
89 89
90 90 def _noopseek(self):
91 91 self._fp.seek(0, os.SEEK_CUR)
92 92
93 93 def seek(self, *args, **kwargs):
94 94 object.__setattr__(self, '_lastop', self.OPNONE)
95 95 return self._fp.seek(*args, **kwargs)
96 96
97 97 def write(self, d):
98 98 if self._lastop == self.OPREAD:
99 99 self._noopseek()
100 100
101 101 object.__setattr__(self, '_lastop', self.OPWRITE)
102 102 return self._fp.write(d)
103 103
104 104 def writelines(self, *args, **kwargs):
105 105 if self._lastop == self.OPREAD:
106 106 self._noopeseek()
107 107
108 108 object.__setattr__(self, '_lastop', self.OPWRITE)
109 109 return self._fp.writelines(*args, **kwargs)
110 110
111 111 def read(self, *args, **kwargs):
112 112 if self._lastop == self.OPWRITE:
113 113 self._noopseek()
114 114
115 115 object.__setattr__(self, '_lastop', self.OPREAD)
116 116 return self._fp.read(*args, **kwargs)
117 117
118 118 def readline(self, *args, **kwargs):
119 119 if self._lastop == self.OPWRITE:
120 120 self._noopseek()
121 121
122 122 object.__setattr__(self, '_lastop', self.OPREAD)
123 123 return self._fp.readline(*args, **kwargs)
124 124
125 125 def readlines(self, *args, **kwargs):
126 126 if self._lastop == self.OPWRITE:
127 127 self._noopseek()
128 128
129 129 object.__setattr__(self, '_lastop', self.OPREAD)
130 130 return self._fp.readlines(*args, **kwargs)
131 131
132 132
133 133 class fdproxy(object):
134 134 """Wraps osutil.posixfile() to override the name attribute to reflect the
135 135 underlying file name.
136 136 """
137 137
138 138 def __init__(self, name, fp):
139 139 self.name = name
140 140 self._fp = fp
141 141
142 142 def __enter__(self):
143 143 self._fp.__enter__()
144 144 # Return this wrapper for the context manager so that the name is
145 145 # still available.
146 146 return self
147 147
148 148 def __exit__(self, exc_type, exc_value, traceback):
149 149 self._fp.__exit__(exc_type, exc_value, traceback)
150 150
151 151 def __iter__(self):
152 152 return iter(self._fp)
153 153
154 154 def __getattr__(self, name):
155 155 return getattr(self._fp, name)
156 156
157 157
158 158 def posixfile(name, mode=b'r', buffering=-1):
159 159 '''Open a file with even more POSIX-like semantics'''
160 160 try:
161 161 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
162 162
163 163 # PyFile_FromFd() ignores the name, and seems to report fp.name as the
164 164 # underlying file descriptor.
165 165 fp = fdproxy(name, fp)
166 166
167 167 # The position when opening in append mode is implementation defined, so
168 168 # make it consistent with other platforms, which position at EOF.
169 169 if b'a' in mode:
170 170 fp.seek(0, os.SEEK_END)
171 171
172 172 if b'+' in mode:
173 173 return mixedfilemodewrapper(fp)
174 174
175 175 return fp
176 176 except WindowsError as err: # pytype: disable=name-error
177 177 # convert to a friendlier exception
178 178 raise IOError(
179 179 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
180 180 )
181 181
182 182
183 183 # may be wrapped by win32mbcs extension
184 184 listdir = osutil.listdir
185 185
186 186
187 187 # copied from .utils.procutil, remove after Python 2 support was dropped
188 188 def _isatty(fp):
189 189 try:
190 190 return fp.isatty()
191 191 except AttributeError:
192 192 return False
193 193
194 194
195 195 def get_password():
196 196 """Prompt for password with echo off, using Windows getch().
197 197
198 198 This shouldn't be called directly- use ``ui.getpass()`` instead, which
199 199 checks if the session is interactive first.
200 200 """
201 201 pw = u""
202 202 while True:
203 203 c = msvcrt.getwch() # pytype: disable=module-attr
204 204 if c == u'\r' or c == u'\n':
205 205 break
206 206 if c == u'\003':
207 207 raise KeyboardInterrupt
208 208 if c == u'\b':
209 209 pw = pw[:-1]
210 210 else:
211 211 pw = pw + c
212 212 msvcrt.putwch(u'\r') # pytype: disable=module-attr
213 213 msvcrt.putwch(u'\n') # pytype: disable=module-attr
214 214 return encoding.unitolocal(pw)
215 215
216 216
217 217 class winstdout(object):
218 218 """Some files on Windows misbehave.
219 219
220 220 When writing to a broken pipe, EINVAL instead of EPIPE may be raised.
221 221
222 222 When writing too many bytes to a console at the same, a "Not enough space"
223 223 error may happen. Python 3 already works around that.
224 224 """
225 225
226 226 def __init__(self, fp):
227 227 self.fp = fp
228 self.throttle = not pycompat.ispy3 and _isatty(fp)
229 228
230 229 def __getattr__(self, key):
231 230 return getattr(self.fp, key)
232 231
233 232 def close(self):
234 233 try:
235 234 self.fp.close()
236 235 except IOError:
237 236 pass
238 237
239 238 def write(self, s):
240 239 try:
241 if not self.throttle:
242 240 return self.fp.write(s)
243 # This is workaround for "Not enough space" error on
244 # writing large size of data to console.
245 limit = 16000
246 l = len(s)
247 start = 0
248 while start < l:
249 end = start + limit
250 self.fp.write(s[start:end])
251 start = end
252 241 except IOError as inst:
253 242 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
254 243 raise
255 244 self.close()
256 245 raise IOError(errno.EPIPE, 'Broken pipe')
257 246
258 247 def flush(self):
259 248 try:
260 249 return self.fp.flush()
261 250 except IOError as inst:
262 251 if not win32.lasterrorwaspipeerror(inst):
263 252 raise
264 253 raise IOError(errno.EPIPE, 'Broken pipe')
265 254
266 255
267 256 def openhardlinks():
268 257 return True
269 258
270 259
271 260 def parsepatchoutput(output_line):
272 261 """parses the output produced by patch and returns the filename"""
273 262 pf = output_line[14:]
274 263 if pf[0] == b'`':
275 264 pf = pf[1:-1] # Remove the quotes
276 265 return pf
277 266
278 267
279 268 def sshargs(sshcmd, host, user, port):
280 269 '''Build argument list for ssh or Plink'''
281 270 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
282 271 args = user and (b"%s@%s" % (user, host)) or host
283 272 if args.startswith(b'-') or args.startswith(b'/'):
284 273 raise error.Abort(
285 274 _(b'illegal ssh hostname or username starting with - or /: %s')
286 275 % args
287 276 )
288 277 args = shellquote(args)
289 278 if port:
290 279 args = b'%s %s %s' % (pflag, shellquote(port), args)
291 280 return args
292 281
293 282
294 283 def setflags(f, l, x):
295 284 pass
296 285
297 286
298 287 def copymode(src, dst, mode=None, enforcewritable=False):
299 288 pass
300 289
301 290
302 291 def checkexec(path):
303 292 return False
304 293
305 294
306 295 def checklink(path):
307 296 return False
308 297
309 298
310 299 def setbinary(fd):
311 300 # When run without console, pipes may expose invalid
312 301 # fileno(), usually set to -1.
313 302 fno = getattr(fd, 'fileno', None)
314 303 if fno is not None and fno() >= 0:
315 304 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
316 305
317 306
318 307 def pconvert(path):
319 308 return path.replace(pycompat.ossep, b'/')
320 309
321 310
322 311 def localpath(path):
323 312 return path.replace(b'/', b'\\')
324 313
325 314
326 315 def normpath(path):
327 316 return pconvert(os.path.normpath(path))
328 317
329 318
330 319 def normcase(path):
331 320 return encoding.upper(path) # NTFS compares via upper()
332 321
333 322
334 323 DRIVE_RE_B = re.compile(b'^[a-z]:')
335 324 DRIVE_RE_S = re.compile('^[a-z]:')
336 325
337 326
338 327 def abspath(path):
339 328 abs_path = os.path.abspath(path) # re-exports
340 329 # Python on Windows is inconsistent regarding the capitalization of drive
341 330 # letter and this cause issue with various path comparison along the way.
342 331 # So we normalize the drive later to upper case here.
343 332 #
344 333 # See https://bugs.python.org/issue40368 for and example of this hell.
345 334 if isinstance(abs_path, bytes):
346 335 if DRIVE_RE_B.match(abs_path):
347 336 abs_path = abs_path[0:1].upper() + abs_path[1:]
348 337 elif DRIVE_RE_S.match(abs_path):
349 338 abs_path = abs_path[0:1].upper() + abs_path[1:]
350 339 return abs_path
351 340
352 341
353 342 # see posix.py for definitions
354 343 normcasespec = encoding.normcasespecs.upper
355 344 normcasefallback = encoding.upperfallback
356 345
357 346
358 347 def samestat(s1, s2):
359 348 return False
360 349
361 350
362 351 def shelltocmdexe(path, env):
363 352 r"""Convert shell variables in the form $var and ${var} inside ``path``
364 353 to %var% form. Existing Windows style variables are left unchanged.
365 354
366 355 The variables are limited to the given environment. Unknown variables are
367 356 left unchanged.
368 357
369 358 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
370 359 >>> # Only valid values are expanded
371 360 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
372 361 ... e)
373 362 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
374 363 >>> # Single quote prevents expansion, as does \$ escaping
375 364 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
376 365 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
377 366 >>> # $$ is not special. %% is not special either, but can be the end and
378 367 >>> # start of consecutive variables
379 368 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
380 369 'cmd $$ %% %var1%%var2%'
381 370 >>> # No double substitution
382 371 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
383 372 '%var1% %var1%'
384 373 >>> # Tilde expansion
385 374 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
386 375 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
387 376 """
388 377 if not any(c in path for c in b"$'~"):
389 378 return path
390 379
391 380 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
392 381
393 382 res = b''
394 383 index = 0
395 384 pathlen = len(path)
396 385 while index < pathlen:
397 386 c = path[index : index + 1]
398 387 if c == b'\'': # no expansion within single quotes
399 388 path = path[index + 1 :]
400 389 pathlen = len(path)
401 390 try:
402 391 index = path.index(b'\'')
403 392 res += b'"' + path[:index] + b'"'
404 393 except ValueError:
405 394 res += c + path
406 395 index = pathlen - 1
407 396 elif c == b'%': # variable
408 397 path = path[index + 1 :]
409 398 pathlen = len(path)
410 399 try:
411 400 index = path.index(b'%')
412 401 except ValueError:
413 402 res += b'%' + path
414 403 index = pathlen - 1
415 404 else:
416 405 var = path[:index]
417 406 res += b'%' + var + b'%'
418 407 elif c == b'$': # variable
419 408 if path[index + 1 : index + 2] == b'{':
420 409 path = path[index + 2 :]
421 410 pathlen = len(path)
422 411 try:
423 412 index = path.index(b'}')
424 413 var = path[:index]
425 414
426 415 # See below for why empty variables are handled specially
427 416 if env.get(var, b'') != b'':
428 417 res += b'%' + var + b'%'
429 418 else:
430 419 res += b'${' + var + b'}'
431 420 except ValueError:
432 421 res += b'${' + path
433 422 index = pathlen - 1
434 423 else:
435 424 var = b''
436 425 index += 1
437 426 c = path[index : index + 1]
438 427 while c != b'' and c in varchars:
439 428 var += c
440 429 index += 1
441 430 c = path[index : index + 1]
442 431 # Some variables (like HG_OLDNODE) may be defined, but have an
443 432 # empty value. Those need to be skipped because when spawning
444 433 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
445 434 # VAR, and that really confuses things like revset expressions.
446 435 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
447 436 # will substitute to an empty string, and everything is happy.
448 437 if env.get(var, b'') != b'':
449 438 res += b'%' + var + b'%'
450 439 else:
451 440 res += b'$' + var
452 441
453 442 if c != b'':
454 443 index -= 1
455 444 elif (
456 445 c == b'~'
457 446 and index + 1 < pathlen
458 447 and path[index + 1 : index + 2] in (b'\\', b'/')
459 448 ):
460 449 res += b"%USERPROFILE%"
461 450 elif (
462 451 c == b'\\'
463 452 and index + 1 < pathlen
464 453 and path[index + 1 : index + 2] in (b'$', b'~')
465 454 ):
466 455 # Skip '\', but only if it is escaping $ or ~
467 456 res += path[index + 1 : index + 2]
468 457 index += 1
469 458 else:
470 459 res += c
471 460
472 461 index += 1
473 462 return res
474 463
475 464
476 465 # A sequence of backslashes is special iff it precedes a double quote:
477 466 # - if there's an even number of backslashes, the double quote is not
478 467 # quoted (i.e. it ends the quoted region)
479 468 # - if there's an odd number of backslashes, the double quote is quoted
480 469 # - in both cases, every pair of backslashes is unquoted into a single
481 470 # backslash
482 471 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
483 472 # So, to quote a string, we must surround it in double quotes, double
484 473 # the number of backslashes that precede double quotes and add another
485 474 # backslash before every double quote (being careful with the double
486 475 # quote we've appended to the end)
487 476 _quotere = None
488 477 _needsshellquote = None
489 478
490 479
491 480 def shellquote(s):
492 481 r"""
493 482 >>> shellquote(br'C:\Users\xyz')
494 483 '"C:\\Users\\xyz"'
495 484 >>> shellquote(br'C:\Users\xyz/mixed')
496 485 '"C:\\Users\\xyz/mixed"'
497 486 >>> # Would be safe not to quote too, since it is all double backslashes
498 487 >>> shellquote(br'C:\\Users\\xyz')
499 488 '"C:\\\\Users\\\\xyz"'
500 489 >>> # But this must be quoted
501 490 >>> shellquote(br'C:\\Users\\xyz/abc')
502 491 '"C:\\\\Users\\\\xyz/abc"'
503 492 """
504 493 global _quotere
505 494 if _quotere is None:
506 495 _quotere = re.compile(br'(\\*)("|\\$)')
507 496 global _needsshellquote
508 497 if _needsshellquote is None:
509 498 # ":" is also treated as "safe character", because it is used as a part
510 499 # of path name on Windows. "\" is also part of a path name, but isn't
511 500 # safe because shlex.split() (kind of) treats it as an escape char and
512 501 # drops it. It will leave the next character, even if it is another
513 502 # "\".
514 503 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
515 504 if s and not _needsshellquote(s) and not _quotere.search(s):
516 505 # "s" shouldn't have to be quoted
517 506 return s
518 507 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
519 508
520 509
521 510 def _unquote(s):
522 511 if s.startswith(b'"') and s.endswith(b'"'):
523 512 return s[1:-1]
524 513 return s
525 514
526 515
527 516 def shellsplit(s):
528 517 """Parse a command string in cmd.exe way (best-effort)"""
529 518 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
530 519
531 520
532 521 # if you change this stub into a real check, please try to implement the
533 522 # username and groupname functions above, too.
534 523 def isowner(st):
535 524 return True
536 525
537 526
538 527 def findexe(command):
539 528 """Find executable for command searching like cmd.exe does.
540 529 If command is a basename then PATH is searched for command.
541 530 PATH isn't searched if command is an absolute or relative path.
542 531 An extension from PATHEXT is found and added if not present.
543 532 If command isn't found None is returned."""
544 533 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
545 534 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
546 535 if os.path.splitext(command)[1].lower() in pathexts:
547 536 pathexts = [b'']
548 537
549 538 def findexisting(pathcommand):
550 539 """Will append extension (if needed) and return existing file"""
551 540 for ext in pathexts:
552 541 executable = pathcommand + ext
553 542 if os.path.exists(executable):
554 543 return executable
555 544 return None
556 545
557 546 if pycompat.ossep in command:
558 547 return findexisting(command)
559 548
560 549 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
561 550 executable = findexisting(os.path.join(path, command))
562 551 if executable is not None:
563 552 return executable
564 553 return findexisting(os.path.expanduser(os.path.expandvars(command)))
565 554
566 555
567 556 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
568 557
569 558
570 559 def statfiles(files):
571 560 """Stat each file in files. Yield each stat, or None if a file
572 561 does not exist or has a type we don't care about.
573 562
574 563 Cluster and cache stat per directory to minimize number of OS stat calls."""
575 564 dircache = {} # dirname -> filename -> status | None if file does not exist
576 565 getkind = stat.S_IFMT
577 566 for nf in files:
578 567 nf = normcase(nf)
579 568 dir, base = os.path.split(nf)
580 569 if not dir:
581 570 dir = b'.'
582 571 cache = dircache.get(dir, None)
583 572 if cache is None:
584 573 try:
585 574 dmap = {
586 575 normcase(n): s
587 576 for n, k, s in listdir(dir, True)
588 577 if getkind(s.st_mode) in _wantedkinds
589 578 }
590 579 except OSError as err:
591 580 # Python >= 2.5 returns ENOENT and adds winerror field
592 581 # EINVAL is raised if dir is not a directory.
593 582 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
594 583 raise
595 584 dmap = {}
596 585 cache = dircache.setdefault(dir, dmap)
597 586 yield cache.get(base, None)
598 587
599 588
600 589 def username(uid=None):
601 590 """Return the name of the user with the given uid.
602 591
603 592 If uid is None, return the name of the current user."""
604 593 if not uid:
605 594 return pycompat.fsencode(getpass.getuser())
606 595 return None
607 596
608 597
609 598 def groupname(gid=None):
610 599 """Return the name of the group with the given gid.
611 600
612 601 If gid is None, return the name of the current group."""
613 602 return None
614 603
615 604
616 605 def readlink(pathname):
617 606 path = pycompat.fsdecode(pathname)
618 607 try:
619 608 link = os.readlink(path)
620 609 except ValueError as e:
621 610 # On py2, os.readlink() raises an AttributeError since it is
622 611 # unsupported. On py3, reading a non-link raises a ValueError. Simply
623 612 # treat this as the error the locking code has been expecting up to now
624 613 # until an effort can be made to enable symlink support on Windows.
625 614 raise AttributeError(e)
626 615 return pycompat.fsencode(link)
627 616
628 617
629 618 def removedirs(name):
630 619 """special version of os.removedirs that does not remove symlinked
631 620 directories or junction points if they actually contain files"""
632 621 if listdir(name):
633 622 return
634 623 os.rmdir(name)
635 624 head, tail = os.path.split(name)
636 625 if not tail:
637 626 head, tail = os.path.split(head)
638 627 while head and tail:
639 628 try:
640 629 if listdir(head):
641 630 return
642 631 os.rmdir(head)
643 632 except (ValueError, OSError):
644 633 break
645 634 head, tail = os.path.split(head)
646 635
647 636
648 637 def rename(src, dst):
649 638 '''atomically rename file src to dst, replacing dst if it exists'''
650 639 try:
651 640 os.rename(src, dst)
652 641 except OSError as e:
653 642 if e.errno != errno.EEXIST:
654 643 raise
655 644 unlink(dst)
656 645 os.rename(src, dst)
657 646
658 647
659 648 def gethgcmd():
660 649 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
661 650
662 651
663 652 def groupmembers(name):
664 653 # Don't support groups on Windows for now
665 654 raise KeyError
666 655
667 656
668 657 def isexec(f):
669 658 return False
670 659
671 660
672 661 class cachestat(object):
673 662 def __init__(self, path):
674 663 pass
675 664
676 665 def cacheable(self):
677 666 return False
678 667
679 668
680 669 def lookupreg(key, valname=None, scope=None):
681 670 """Look up a key/value name in the Windows registry.
682 671
683 672 valname: value name. If unspecified, the default value for the key
684 673 is used.
685 674 scope: optionally specify scope for registry lookup, this can be
686 675 a sequence of scopes to look up in order. Default (CURRENT_USER,
687 676 LOCAL_MACHINE).
688 677 """
689 678 if scope is None:
690 679 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
691 680 elif not isinstance(scope, (list, tuple)):
692 681 scope = (scope,)
693 682 for s in scope:
694 683 try:
695 684 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
696 685 name = valname and encoding.strfromlocal(valname) or valname
697 686 val = winreg.QueryValueEx(hkey, name)[0]
698 687 # never let a Unicode string escape into the wild
699 688 return encoding.unitolocal(val)
700 689 except EnvironmentError:
701 690 pass
702 691
703 692
704 693 expandglobs = True
705 694
706 695
707 696 def statislink(st):
708 697 '''check whether a stat result is a symlink'''
709 698 return False
710 699
711 700
712 701 def statisexec(st):
713 702 '''check whether a stat result is an executable file'''
714 703 return False
715 704
716 705
717 706 def poll(fds):
718 707 # see posix.py for description
719 708 raise NotImplementedError()
720 709
721 710
722 711 def readpipe(pipe):
723 712 """Read all available data from a pipe."""
724 713 chunks = []
725 714 while True:
726 715 size = win32.peekpipe(pipe)
727 716 if not size:
728 717 break
729 718
730 719 s = pipe.read(size)
731 720 if not s:
732 721 break
733 722 chunks.append(s)
734 723
735 724 return b''.join(chunks)
736 725
737 726
738 727 def bindunixsocket(sock, path):
739 728 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now