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