##// END OF EJS Templates
windows: prevent bytes from being passed to registry APIs...
Matt Harbison -
r50212:709e5f7e default
parent child Browse files
Show More
@@ -1,729 +1,731 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:
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:
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:
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 228
229 229 def __getattr__(self, key):
230 230 return getattr(self.fp, key)
231 231
232 232 def close(self):
233 233 try:
234 234 self.fp.close()
235 235 except IOError:
236 236 pass
237 237
238 238 def write(self, s):
239 239 try:
240 240 return self.fp.write(s)
241 241 except IOError as inst:
242 242 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
243 243 raise
244 244 self.close()
245 245 raise IOError(errno.EPIPE, 'Broken pipe')
246 246
247 247 def flush(self):
248 248 try:
249 249 return self.fp.flush()
250 250 except IOError as inst:
251 251 if not win32.lasterrorwaspipeerror(inst):
252 252 raise
253 253 raise IOError(errno.EPIPE, 'Broken pipe')
254 254
255 255
256 256 def openhardlinks():
257 257 return True
258 258
259 259
260 260 def parsepatchoutput(output_line):
261 261 """parses the output produced by patch and returns the filename"""
262 262 pf = output_line[14:]
263 263 if pf[0] == b'`':
264 264 pf = pf[1:-1] # Remove the quotes
265 265 return pf
266 266
267 267
268 268 def sshargs(sshcmd, host, user, port):
269 269 '''Build argument list for ssh or Plink'''
270 270 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
271 271 args = user and (b"%s@%s" % (user, host)) or host
272 272 if args.startswith(b'-') or args.startswith(b'/'):
273 273 raise error.Abort(
274 274 _(b'illegal ssh hostname or username starting with - or /: %s')
275 275 % args
276 276 )
277 277 args = shellquote(args)
278 278 if port:
279 279 args = b'%s %s %s' % (pflag, shellquote(port), args)
280 280 return args
281 281
282 282
283 283 def setflags(f, l, x):
284 284 pass
285 285
286 286
287 287 def copymode(src, dst, mode=None, enforcewritable=False):
288 288 pass
289 289
290 290
291 291 def checkexec(path):
292 292 return False
293 293
294 294
295 295 def checklink(path):
296 296 return False
297 297
298 298
299 299 def setbinary(fd):
300 300 # When run without console, pipes may expose invalid
301 301 # fileno(), usually set to -1.
302 302 fno = getattr(fd, 'fileno', None)
303 303 if fno is not None and fno() >= 0:
304 304 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
305 305
306 306
307 307 def pconvert(path):
308 308 return path.replace(pycompat.ossep, b'/')
309 309
310 310
311 311 def localpath(path):
312 312 return path.replace(b'/', b'\\')
313 313
314 314
315 315 def normpath(path):
316 316 return pconvert(os.path.normpath(path))
317 317
318 318
319 319 def normcase(path):
320 320 return encoding.upper(path) # NTFS compares via upper()
321 321
322 322
323 323 DRIVE_RE_B = re.compile(b'^[a-z]:')
324 324 DRIVE_RE_S = re.compile('^[a-z]:')
325 325
326 326
327 327 def abspath(path):
328 328 abs_path = os.path.abspath(path) # re-exports
329 329 # Python on Windows is inconsistent regarding the capitalization of drive
330 330 # letter and this cause issue with various path comparison along the way.
331 331 # So we normalize the drive later to upper case here.
332 332 #
333 333 # See https://bugs.python.org/issue40368 for and example of this hell.
334 334 if isinstance(abs_path, bytes):
335 335 if DRIVE_RE_B.match(abs_path):
336 336 abs_path = abs_path[0:1].upper() + abs_path[1:]
337 337 elif DRIVE_RE_S.match(abs_path):
338 338 abs_path = abs_path[0:1].upper() + abs_path[1:]
339 339 return abs_path
340 340
341 341
342 342 # see posix.py for definitions
343 343 normcasespec = encoding.normcasespecs.upper
344 344 normcasefallback = encoding.upperfallback
345 345
346 346
347 347 def samestat(s1, s2):
348 348 return False
349 349
350 350
351 351 def shelltocmdexe(path, env):
352 352 r"""Convert shell variables in the form $var and ${var} inside ``path``
353 353 to %var% form. Existing Windows style variables are left unchanged.
354 354
355 355 The variables are limited to the given environment. Unknown variables are
356 356 left unchanged.
357 357
358 358 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
359 359 >>> # Only valid values are expanded
360 360 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
361 361 ... e)
362 362 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
363 363 >>> # Single quote prevents expansion, as does \$ escaping
364 364 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
365 365 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
366 366 >>> # $$ is not special. %% is not special either, but can be the end and
367 367 >>> # start of consecutive variables
368 368 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
369 369 'cmd $$ %% %var1%%var2%'
370 370 >>> # No double substitution
371 371 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
372 372 '%var1% %var1%'
373 373 >>> # Tilde expansion
374 374 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
375 375 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
376 376 """
377 377 if not any(c in path for c in b"$'~"):
378 378 return path
379 379
380 380 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
381 381
382 382 res = b''
383 383 index = 0
384 384 pathlen = len(path)
385 385 while index < pathlen:
386 386 c = path[index : index + 1]
387 387 if c == b'\'': # no expansion within single quotes
388 388 path = path[index + 1 :]
389 389 pathlen = len(path)
390 390 try:
391 391 index = path.index(b'\'')
392 392 res += b'"' + path[:index] + b'"'
393 393 except ValueError:
394 394 res += c + path
395 395 index = pathlen - 1
396 396 elif c == b'%': # variable
397 397 path = path[index + 1 :]
398 398 pathlen = len(path)
399 399 try:
400 400 index = path.index(b'%')
401 401 except ValueError:
402 402 res += b'%' + path
403 403 index = pathlen - 1
404 404 else:
405 405 var = path[:index]
406 406 res += b'%' + var + b'%'
407 407 elif c == b'$': # variable
408 408 if path[index + 1 : index + 2] == b'{':
409 409 path = path[index + 2 :]
410 410 pathlen = len(path)
411 411 try:
412 412 index = path.index(b'}')
413 413 var = path[:index]
414 414
415 415 # See below for why empty variables are handled specially
416 416 if env.get(var, b'') != b'':
417 417 res += b'%' + var + b'%'
418 418 else:
419 419 res += b'${' + var + b'}'
420 420 except ValueError:
421 421 res += b'${' + path
422 422 index = pathlen - 1
423 423 else:
424 424 var = b''
425 425 index += 1
426 426 c = path[index : index + 1]
427 427 while c != b'' and c in varchars:
428 428 var += c
429 429 index += 1
430 430 c = path[index : index + 1]
431 431 # Some variables (like HG_OLDNODE) may be defined, but have an
432 432 # empty value. Those need to be skipped because when spawning
433 433 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
434 434 # VAR, and that really confuses things like revset expressions.
435 435 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
436 436 # will substitute to an empty string, and everything is happy.
437 437 if env.get(var, b'') != b'':
438 438 res += b'%' + var + b'%'
439 439 else:
440 440 res += b'$' + var
441 441
442 442 if c != b'':
443 443 index -= 1
444 444 elif (
445 445 c == b'~'
446 446 and index + 1 < pathlen
447 447 and path[index + 1 : index + 2] in (b'\\', b'/')
448 448 ):
449 449 res += b"%USERPROFILE%"
450 450 elif (
451 451 c == b'\\'
452 452 and index + 1 < pathlen
453 453 and path[index + 1 : index + 2] in (b'$', b'~')
454 454 ):
455 455 # Skip '\', but only if it is escaping $ or ~
456 456 res += path[index + 1 : index + 2]
457 457 index += 1
458 458 else:
459 459 res += c
460 460
461 461 index += 1
462 462 return res
463 463
464 464
465 465 # A sequence of backslashes is special iff it precedes a double quote:
466 466 # - if there's an even number of backslashes, the double quote is not
467 467 # quoted (i.e. it ends the quoted region)
468 468 # - if there's an odd number of backslashes, the double quote is quoted
469 469 # - in both cases, every pair of backslashes is unquoted into a single
470 470 # backslash
471 471 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
472 472 # So, to quote a string, we must surround it in double quotes, double
473 473 # the number of backslashes that precede double quotes and add another
474 474 # backslash before every double quote (being careful with the double
475 475 # quote we've appended to the end)
476 476 _quotere = None
477 477 _needsshellquote = None
478 478
479 479
480 480 def shellquote(s):
481 481 r"""
482 482 >>> shellquote(br'C:\Users\xyz')
483 483 '"C:\\Users\\xyz"'
484 484 >>> shellquote(br'C:\Users\xyz/mixed')
485 485 '"C:\\Users\\xyz/mixed"'
486 486 >>> # Would be safe not to quote too, since it is all double backslashes
487 487 >>> shellquote(br'C:\\Users\\xyz')
488 488 '"C:\\\\Users\\\\xyz"'
489 489 >>> # But this must be quoted
490 490 >>> shellquote(br'C:\\Users\\xyz/abc')
491 491 '"C:\\\\Users\\\\xyz/abc"'
492 492 """
493 493 global _quotere
494 494 if _quotere is None:
495 495 _quotere = re.compile(br'(\\*)("|\\$)')
496 496 global _needsshellquote
497 497 if _needsshellquote is None:
498 498 # ":" is also treated as "safe character", because it is used as a part
499 499 # of path name on Windows. "\" is also part of a path name, but isn't
500 500 # safe because shlex.split() (kind of) treats it as an escape char and
501 501 # drops it. It will leave the next character, even if it is another
502 502 # "\".
503 503 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
504 504 if s and not _needsshellquote(s) and not _quotere.search(s):
505 505 # "s" shouldn't have to be quoted
506 506 return s
507 507 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
508 508
509 509
510 510 def _unquote(s):
511 511 if s.startswith(b'"') and s.endswith(b'"'):
512 512 return s[1:-1]
513 513 return s
514 514
515 515
516 516 def shellsplit(s):
517 517 """Parse a command string in cmd.exe way (best-effort)"""
518 518 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
519 519
520 520
521 521 # if you change this stub into a real check, please try to implement the
522 522 # username and groupname functions above, too.
523 523 def isowner(st):
524 524 return True
525 525
526 526
527 527 def findexe(command):
528 528 """Find executable for command searching like cmd.exe does.
529 529 If command is a basename then PATH is searched for command.
530 530 PATH isn't searched if command is an absolute or relative path.
531 531 An extension from PATHEXT is found and added if not present.
532 532 If command isn't found None is returned."""
533 533 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
534 534 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
535 535 if os.path.splitext(command)[1].lower() in pathexts:
536 536 pathexts = [b'']
537 537
538 538 def findexisting(pathcommand):
539 539 """Will append extension (if needed) and return existing file"""
540 540 for ext in pathexts:
541 541 executable = pathcommand + ext
542 542 if os.path.exists(executable):
543 543 return executable
544 544 return None
545 545
546 546 if pycompat.ossep in command:
547 547 return findexisting(command)
548 548
549 549 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
550 550 executable = findexisting(os.path.join(path, command))
551 551 if executable is not None:
552 552 return executable
553 553 return findexisting(os.path.expanduser(os.path.expandvars(command)))
554 554
555 555
556 556 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
557 557
558 558
559 559 def statfiles(files):
560 560 """Stat each file in files. Yield each stat, or None if a file
561 561 does not exist or has a type we don't care about.
562 562
563 563 Cluster and cache stat per directory to minimize number of OS stat calls."""
564 564 dircache = {} # dirname -> filename -> status | None if file does not exist
565 565 getkind = stat.S_IFMT
566 566 for nf in files:
567 567 nf = normcase(nf)
568 568 dir, base = os.path.split(nf)
569 569 if not dir:
570 570 dir = b'.'
571 571 cache = dircache.get(dir, None)
572 572 if cache is None:
573 573 try:
574 574 dmap = {
575 575 normcase(n): s
576 576 for n, k, s in listdir(dir, True)
577 577 if getkind(s.st_mode) in _wantedkinds
578 578 }
579 579 except (FileNotFoundError, NotADirectoryError):
580 580 dmap = {}
581 581 cache = dircache.setdefault(dir, dmap)
582 582 yield cache.get(base, None)
583 583
584 584
585 585 def username(uid=None):
586 586 """Return the name of the user with the given uid.
587 587
588 588 If uid is None, return the name of the current user."""
589 589 if not uid:
590 590 return pycompat.fsencode(getpass.getuser())
591 591 return None
592 592
593 593
594 594 def groupname(gid=None):
595 595 """Return the name of the group with the given gid.
596 596
597 597 If gid is None, return the name of the current group."""
598 598 return None
599 599
600 600
601 601 def readlink(pathname):
602 602 path = pycompat.fsdecode(pathname)
603 603 try:
604 604 link = os.readlink(path)
605 605 except ValueError as e:
606 606 # On py2, os.readlink() raises an AttributeError since it is
607 607 # unsupported. On py3, reading a non-link raises a ValueError. Simply
608 608 # treat this as the error the locking code has been expecting up to now
609 609 # until an effort can be made to enable symlink support on Windows.
610 610 raise AttributeError(e)
611 611 return pycompat.fsencode(link)
612 612
613 613
614 614 def removedirs(name):
615 615 """special version of os.removedirs that does not remove symlinked
616 616 directories or junction points if they actually contain files"""
617 617 if listdir(name):
618 618 return
619 619 os.rmdir(name)
620 620 head, tail = os.path.split(name)
621 621 if not tail:
622 622 head, tail = os.path.split(head)
623 623 while head and tail:
624 624 try:
625 625 if listdir(head):
626 626 return
627 627 os.rmdir(head)
628 628 except (ValueError, OSError):
629 629 break
630 630 head, tail = os.path.split(head)
631 631
632 632
633 633 def rename(src, dst):
634 634 '''atomically rename file src to dst, replacing dst if it exists'''
635 635 try:
636 636 os.rename(src, dst)
637 637 except FileExistsError:
638 638 unlink(dst)
639 639 os.rename(src, dst)
640 640
641 641
642 642 def gethgcmd():
643 643 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
644 644
645 645
646 646 def groupmembers(name):
647 647 # Don't support groups on Windows for now
648 648 raise KeyError
649 649
650 650
651 651 def isexec(f):
652 652 return False
653 653
654 654
655 655 class cachestat:
656 656 def __init__(self, path):
657 657 pass
658 658
659 659 def cacheable(self):
660 660 return False
661 661
662 662
663 663 def lookupreg(key, valname=None, scope=None):
664 664 """Look up a key/value name in the Windows registry.
665 665
666 666 valname: value name. If unspecified, the default value for the key
667 667 is used.
668 668 scope: optionally specify scope for registry lookup, this can be
669 669 a sequence of scopes to look up in order. Default (CURRENT_USER,
670 670 LOCAL_MACHINE).
671 671 """
672 672 if scope is None:
673 673 # pytype: disable=module-attr
674 674 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
675 675 # pytype: enable=module-attr
676 676 elif not isinstance(scope, (list, tuple)):
677 677 scope = (scope,)
678 678 for s in scope:
679 679 try:
680 680 # pytype: disable=module-attr
681 681 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
682 682 # pytype: enable=module-attr
683 name = valname and encoding.strfromlocal(valname) or valname
683 name = None
684 if valname is not None:
685 name = encoding.strfromlocal(valname)
684 686 # pytype: disable=module-attr
685 687 val = winreg.QueryValueEx(hkey, name)[0]
686 688 # pytype: enable=module-attr
687 689
688 690 # never let a Unicode string escape into the wild
689 691 return encoding.unitolocal(val)
690 692 except EnvironmentError:
691 693 pass
692 694
693 695
694 696 expandglobs = True
695 697
696 698
697 699 def statislink(st):
698 700 '''check whether a stat result is a symlink'''
699 701 return False
700 702
701 703
702 704 def statisexec(st):
703 705 '''check whether a stat result is an executable file'''
704 706 return False
705 707
706 708
707 709 def poll(fds):
708 710 # see posix.py for description
709 711 raise NotImplementedError()
710 712
711 713
712 714 def readpipe(pipe):
713 715 """Read all available data from a pipe."""
714 716 chunks = []
715 717 while True:
716 718 size = win32.peekpipe(pipe)
717 719 if not size:
718 720 break
719 721
720 722 s = pipe.read(size)
721 723 if not s:
722 724 break
723 725 chunks.append(s)
724 726
725 727 return b''.join(chunks)
726 728
727 729
728 730 def bindunixsocket(sock, path):
729 731 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now