##// END OF EJS Templates
windows: suppress pytype warnings for Windows imports and functions...
Matt Harbison -
r44207:fe73ec69 default
parent child Browse files
Show More
@@ -1,506 +1,506 b''
1 1 # pycompat.py - portability shim for python 3
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 """Mercurial portability shim for python 3.
7 7
8 8 This contains aliases to hide python version-specific details from the core.
9 9 """
10 10
11 11 from __future__ import absolute_import
12 12
13 13 import getopt
14 14 import inspect
15 15 import json
16 16 import os
17 17 import shlex
18 18 import sys
19 19 import tempfile
20 20
21 21 ispy3 = sys.version_info[0] >= 3
22 22 ispypy = '__pypy__' in sys.builtin_module_names
23 23
24 24 if not ispy3:
25 25 import cookielib
26 26 import cPickle as pickle
27 27 import httplib
28 28 import Queue as queue
29 29 import SocketServer as socketserver
30 30 import xmlrpclib
31 31
32 32 from .thirdparty.concurrent import futures
33 33
34 34 def future_set_exception_info(f, exc_info):
35 35 f.set_exception_info(*exc_info)
36 36
37 37
38 38 else:
39 39 import concurrent.futures as futures
40 40 import http.cookiejar as cookielib
41 41 import http.client as httplib
42 42 import pickle
43 43 import queue as queue
44 44 import socketserver
45 45 import xmlrpc.client as xmlrpclib
46 46
47 47 def future_set_exception_info(f, exc_info):
48 48 f.set_exception(exc_info[0])
49 49
50 50
51 51 def identity(a):
52 52 return a
53 53
54 54
55 55 def _rapply(f, xs):
56 56 if xs is None:
57 57 # assume None means non-value of optional data
58 58 return xs
59 59 if isinstance(xs, (list, set, tuple)):
60 60 return type(xs)(_rapply(f, x) for x in xs)
61 61 if isinstance(xs, dict):
62 62 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
63 63 return f(xs)
64 64
65 65
66 66 def rapply(f, xs):
67 67 """Apply function recursively to every item preserving the data structure
68 68
69 69 >>> def f(x):
70 70 ... return 'f(%s)' % x
71 71 >>> rapply(f, None) is None
72 72 True
73 73 >>> rapply(f, 'a')
74 74 'f(a)'
75 75 >>> rapply(f, {'a'}) == {'f(a)'}
76 76 True
77 77 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
78 78 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
79 79
80 80 >>> xs = [object()]
81 81 >>> rapply(identity, xs) is xs
82 82 True
83 83 """
84 84 if f is identity:
85 85 # fast path mainly for py2
86 86 return xs
87 87 return _rapply(f, xs)
88 88
89 89
90 90 if ispy3:
91 91 import builtins
92 92 import codecs
93 93 import functools
94 94 import io
95 95 import struct
96 96
97 97 if os.name == r'nt' and sys.version_info >= (3, 6):
98 98 # MBCS (or ANSI) filesystem encoding must be used as before.
99 99 # Otherwise non-ASCII filenames in existing repositories would be
100 100 # corrupted.
101 101 # This must be set once prior to any fsencode/fsdecode calls.
102 sys._enablelegacywindowsfsencoding()
102 sys._enablelegacywindowsfsencoding() # pytype: disable=module-attr
103 103
104 104 fsencode = os.fsencode
105 105 fsdecode = os.fsdecode
106 106 oscurdir = os.curdir.encode('ascii')
107 107 oslinesep = os.linesep.encode('ascii')
108 108 osname = os.name.encode('ascii')
109 109 ospathsep = os.pathsep.encode('ascii')
110 110 ospardir = os.pardir.encode('ascii')
111 111 ossep = os.sep.encode('ascii')
112 112 osaltsep = os.altsep
113 113 if osaltsep:
114 114 osaltsep = osaltsep.encode('ascii')
115 115
116 116 sysplatform = sys.platform.encode('ascii')
117 117 sysexecutable = sys.executable
118 118 if sysexecutable:
119 119 sysexecutable = os.fsencode(sysexecutable)
120 120 bytesio = io.BytesIO
121 121 # TODO deprecate stringio name, as it is a lie on Python 3.
122 122 stringio = bytesio
123 123
124 124 def maplist(*args):
125 125 return list(map(*args))
126 126
127 127 def rangelist(*args):
128 128 return list(range(*args))
129 129
130 130 def ziplist(*args):
131 131 return list(zip(*args))
132 132
133 133 rawinput = input
134 134 getargspec = inspect.getfullargspec
135 135
136 136 long = int
137 137
138 138 # TODO: .buffer might not exist if std streams were replaced; we'll need
139 139 # a silly wrapper to make a bytes stream backed by a unicode one.
140 140 stdin = sys.stdin.buffer
141 141 stdout = sys.stdout.buffer
142 142 stderr = sys.stderr.buffer
143 143
144 144 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
145 145 # we can use os.fsencode() to get back bytes argv.
146 146 #
147 147 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
148 148 #
149 149 # On Windows, the native argv is unicode and is converted to MBCS bytes
150 150 # since we do enable the legacy filesystem encoding.
151 151 if getattr(sys, 'argv', None) is not None:
152 152 sysargv = list(map(os.fsencode, sys.argv))
153 153
154 154 bytechr = struct.Struct('>B').pack
155 155 byterepr = b'%r'.__mod__
156 156
157 157 class bytestr(bytes):
158 158 """A bytes which mostly acts as a Python 2 str
159 159
160 160 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
161 161 ('', 'foo', 'ascii', '1')
162 162 >>> s = bytestr(b'foo')
163 163 >>> assert s is bytestr(s)
164 164
165 165 __bytes__() should be called if provided:
166 166
167 167 >>> class bytesable(object):
168 168 ... def __bytes__(self):
169 169 ... return b'bytes'
170 170 >>> bytestr(bytesable())
171 171 'bytes'
172 172
173 173 There's no implicit conversion from non-ascii str as its encoding is
174 174 unknown:
175 175
176 176 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
177 177 Traceback (most recent call last):
178 178 ...
179 179 UnicodeEncodeError: ...
180 180
181 181 Comparison between bytestr and bytes should work:
182 182
183 183 >>> assert bytestr(b'foo') == b'foo'
184 184 >>> assert b'foo' == bytestr(b'foo')
185 185 >>> assert b'f' in bytestr(b'foo')
186 186 >>> assert bytestr(b'f') in b'foo'
187 187
188 188 Sliced elements should be bytes, not integer:
189 189
190 190 >>> s[1], s[:2]
191 191 (b'o', b'fo')
192 192 >>> list(s), list(reversed(s))
193 193 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
194 194
195 195 As bytestr type isn't propagated across operations, you need to cast
196 196 bytes to bytestr explicitly:
197 197
198 198 >>> s = bytestr(b'foo').upper()
199 199 >>> t = bytestr(s)
200 200 >>> s[0], t[0]
201 201 (70, b'F')
202 202
203 203 Be careful to not pass a bytestr object to a function which expects
204 204 bytearray-like behavior.
205 205
206 206 >>> t = bytes(t) # cast to bytes
207 207 >>> assert type(t) is bytes
208 208 """
209 209
210 210 def __new__(cls, s=b''):
211 211 if isinstance(s, bytestr):
212 212 return s
213 213 if not isinstance(
214 214 s, (bytes, bytearray)
215 215 ) and not hasattr( # hasattr-py3-only
216 216 s, u'__bytes__'
217 217 ):
218 218 s = str(s).encode('ascii')
219 219 return bytes.__new__(cls, s)
220 220
221 221 def __getitem__(self, key):
222 222 s = bytes.__getitem__(self, key)
223 223 if not isinstance(s, bytes):
224 224 s = bytechr(s)
225 225 return s
226 226
227 227 def __iter__(self):
228 228 return iterbytestr(bytes.__iter__(self))
229 229
230 230 def __repr__(self):
231 231 return bytes.__repr__(self)[1:] # drop b''
232 232
233 233 def iterbytestr(s):
234 234 """Iterate bytes as if it were a str object of Python 2"""
235 235 return map(bytechr, s)
236 236
237 237 def maybebytestr(s):
238 238 """Promote bytes to bytestr"""
239 239 if isinstance(s, bytes):
240 240 return bytestr(s)
241 241 return s
242 242
243 243 def sysbytes(s):
244 244 """Convert an internal str (e.g. keyword, __doc__) back to bytes
245 245
246 246 This never raises UnicodeEncodeError, but only ASCII characters
247 247 can be round-trip by sysstr(sysbytes(s)).
248 248 """
249 249 return s.encode('utf-8')
250 250
251 251 def sysstr(s):
252 252 """Return a keyword str to be passed to Python functions such as
253 253 getattr() and str.encode()
254 254
255 255 This never raises UnicodeDecodeError. Non-ascii characters are
256 256 considered invalid and mapped to arbitrary but unique code points
257 257 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
258 258 """
259 259 if isinstance(s, builtins.str):
260 260 return s
261 261 return s.decode('latin-1')
262 262
263 263 def strurl(url):
264 264 """Converts a bytes url back to str"""
265 265 if isinstance(url, bytes):
266 266 return url.decode('ascii')
267 267 return url
268 268
269 269 def bytesurl(url):
270 270 """Converts a str url to bytes by encoding in ascii"""
271 271 if isinstance(url, str):
272 272 return url.encode('ascii')
273 273 return url
274 274
275 275 def raisewithtb(exc, tb):
276 276 """Raise exception with the given traceback"""
277 277 raise exc.with_traceback(tb)
278 278
279 279 def getdoc(obj):
280 280 """Get docstring as bytes; may be None so gettext() won't confuse it
281 281 with _('')"""
282 282 doc = getattr(obj, '__doc__', None)
283 283 if doc is None:
284 284 return doc
285 285 return sysbytes(doc)
286 286
287 287 def _wrapattrfunc(f):
288 288 @functools.wraps(f)
289 289 def w(object, name, *args):
290 290 return f(object, sysstr(name), *args)
291 291
292 292 return w
293 293
294 294 # these wrappers are automagically imported by hgloader
295 295 delattr = _wrapattrfunc(builtins.delattr)
296 296 getattr = _wrapattrfunc(builtins.getattr)
297 297 hasattr = _wrapattrfunc(builtins.hasattr)
298 298 setattr = _wrapattrfunc(builtins.setattr)
299 299 xrange = builtins.range
300 300 unicode = str
301 301
302 302 def open(name, mode=b'r', buffering=-1, encoding=None):
303 303 return builtins.open(name, sysstr(mode), buffering, encoding)
304 304
305 305 safehasattr = _wrapattrfunc(builtins.hasattr)
306 306
307 307 def _getoptbwrapper(orig, args, shortlist, namelist):
308 308 """
309 309 Takes bytes arguments, converts them to unicode, pass them to
310 310 getopt.getopt(), convert the returned values back to bytes and then
311 311 return them for Python 3 compatibility as getopt.getopt() don't accepts
312 312 bytes on Python 3.
313 313 """
314 314 args = [a.decode('latin-1') for a in args]
315 315 shortlist = shortlist.decode('latin-1')
316 316 namelist = [a.decode('latin-1') for a in namelist]
317 317 opts, args = orig(args, shortlist, namelist)
318 318 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1')) for a in opts]
319 319 args = [a.encode('latin-1') for a in args]
320 320 return opts, args
321 321
322 322 def strkwargs(dic):
323 323 """
324 324 Converts the keys of a python dictonary to str i.e. unicodes so that
325 325 they can be passed as keyword arguments as dictonaries with bytes keys
326 326 can't be passed as keyword arguments to functions on Python 3.
327 327 """
328 328 dic = dict((k.decode('latin-1'), v) for k, v in dic.items())
329 329 return dic
330 330
331 331 def byteskwargs(dic):
332 332 """
333 333 Converts keys of python dictonaries to bytes as they were converted to
334 334 str to pass that dictonary as a keyword argument on Python 3.
335 335 """
336 336 dic = dict((k.encode('latin-1'), v) for k, v in dic.items())
337 337 return dic
338 338
339 339 # TODO: handle shlex.shlex().
340 340 def shlexsplit(s, comments=False, posix=True):
341 341 """
342 342 Takes bytes argument, convert it to str i.e. unicodes, pass that into
343 343 shlex.split(), convert the returned value to bytes and return that for
344 344 Python 3 compatibility as shelx.split() don't accept bytes on Python 3.
345 345 """
346 346 ret = shlex.split(s.decode('latin-1'), comments, posix)
347 347 return [a.encode('latin-1') for a in ret]
348 348
349 349 iteritems = lambda x: x.items()
350 350 itervalues = lambda x: x.values()
351 351
352 352 # Python 3.5's json.load and json.loads require str. We polyfill its
353 353 # code for detecting encoding from bytes.
354 354 if sys.version_info[0:2] < (3, 6):
355 355
356 356 def _detect_encoding(b):
357 357 bstartswith = b.startswith
358 358 if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)):
359 359 return 'utf-32'
360 360 if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)):
361 361 return 'utf-16'
362 362 if bstartswith(codecs.BOM_UTF8):
363 363 return 'utf-8-sig'
364 364
365 365 if len(b) >= 4:
366 366 if not b[0]:
367 367 # 00 00 -- -- - utf-32-be
368 368 # 00 XX -- -- - utf-16-be
369 369 return 'utf-16-be' if b[1] else 'utf-32-be'
370 370 if not b[1]:
371 371 # XX 00 00 00 - utf-32-le
372 372 # XX 00 00 XX - utf-16-le
373 373 # XX 00 XX -- - utf-16-le
374 374 return 'utf-16-le' if b[2] or b[3] else 'utf-32-le'
375 375 elif len(b) == 2:
376 376 if not b[0]:
377 377 # 00 XX - utf-16-be
378 378 return 'utf-16-be'
379 379 if not b[1]:
380 380 # XX 00 - utf-16-le
381 381 return 'utf-16-le'
382 382 # default
383 383 return 'utf-8'
384 384
385 385 def json_loads(s, *args, **kwargs):
386 386 if isinstance(s, (bytes, bytearray)):
387 387 s = s.decode(_detect_encoding(s), 'surrogatepass')
388 388
389 389 return json.loads(s, *args, **kwargs)
390 390
391 391 else:
392 392 json_loads = json.loads
393 393
394 394 else:
395 395 import cStringIO
396 396
397 397 xrange = xrange
398 398 unicode = unicode
399 399 bytechr = chr
400 400 byterepr = repr
401 401 bytestr = str
402 402 iterbytestr = iter
403 403 maybebytestr = identity
404 404 sysbytes = identity
405 405 sysstr = identity
406 406 strurl = identity
407 407 bytesurl = identity
408 408 open = open
409 409 delattr = delattr
410 410 getattr = getattr
411 411 hasattr = hasattr
412 412 setattr = setattr
413 413
414 414 # this can't be parsed on Python 3
415 415 exec(b'def raisewithtb(exc, tb):\n raise exc, None, tb\n')
416 416
417 417 def fsencode(filename):
418 418 """
419 419 Partial backport from os.py in Python 3, which only accepts bytes.
420 420 In Python 2, our paths should only ever be bytes, a unicode path
421 421 indicates a bug.
422 422 """
423 423 if isinstance(filename, str):
424 424 return filename
425 425 else:
426 426 raise TypeError("expect str, not %s" % type(filename).__name__)
427 427
428 428 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
429 429 # better not to touch Python 2 part as it's already working fine.
430 430 fsdecode = identity
431 431
432 432 def getdoc(obj):
433 433 return getattr(obj, '__doc__', None)
434 434
435 435 _notset = object()
436 436
437 437 def safehasattr(thing, attr):
438 438 return getattr(thing, attr, _notset) is not _notset
439 439
440 440 def _getoptbwrapper(orig, args, shortlist, namelist):
441 441 return orig(args, shortlist, namelist)
442 442
443 443 strkwargs = identity
444 444 byteskwargs = identity
445 445
446 446 oscurdir = os.curdir
447 447 oslinesep = os.linesep
448 448 osname = os.name
449 449 ospathsep = os.pathsep
450 450 ospardir = os.pardir
451 451 ossep = os.sep
452 452 osaltsep = os.altsep
453 453 long = long
454 454 stdin = sys.stdin
455 455 stdout = sys.stdout
456 456 stderr = sys.stderr
457 457 if getattr(sys, 'argv', None) is not None:
458 458 sysargv = sys.argv
459 459 sysplatform = sys.platform
460 460 sysexecutable = sys.executable
461 461 shlexsplit = shlex.split
462 462 bytesio = cStringIO.StringIO
463 463 stringio = bytesio
464 464 maplist = map
465 465 rangelist = range
466 466 ziplist = zip
467 467 rawinput = raw_input
468 468 getargspec = inspect.getargspec
469 469 iteritems = lambda x: x.iteritems()
470 470 itervalues = lambda x: x.itervalues()
471 471 json_loads = json.loads
472 472
473 473 isjython = sysplatform.startswith(b'java')
474 474
475 475 isdarwin = sysplatform.startswith(b'darwin')
476 476 islinux = sysplatform.startswith(b'linux')
477 477 isposix = osname == b'posix'
478 478 iswindows = osname == b'nt'
479 479
480 480
481 481 def getoptb(args, shortlist, namelist):
482 482 return _getoptbwrapper(getopt.getopt, args, shortlist, namelist)
483 483
484 484
485 485 def gnugetoptb(args, shortlist, namelist):
486 486 return _getoptbwrapper(getopt.gnu_getopt, args, shortlist, namelist)
487 487
488 488
489 489 def mkdtemp(suffix=b'', prefix=b'tmp', dir=None):
490 490 return tempfile.mkdtemp(suffix, prefix, dir)
491 491
492 492
493 493 # text=True is not supported; use util.from/tonativeeol() instead
494 494 def mkstemp(suffix=b'', prefix=b'tmp', dir=None):
495 495 return tempfile.mkstemp(suffix, prefix, dir)
496 496
497 497
498 498 # mode must include 'b'ytes as encoding= is not supported
499 499 def namedtempfile(
500 500 mode=b'w+b', bufsize=-1, suffix=b'', prefix=b'tmp', dir=None, delete=True
501 501 ):
502 502 mode = sysstr(mode)
503 503 assert 'b' in mode
504 504 return tempfile.NamedTemporaryFile(
505 505 mode, bufsize, suffix=suffix, prefix=prefix, dir=dir, delete=delete
506 506 )
@@ -1,65 +1,66 b''
1 1 from __future__ import absolute_import
2 2
3 3 import os
4 4
5 5 from . import (
6 6 encoding,
7 7 pycompat,
8 8 util,
9 9 win32,
10 10 )
11 11
12 12 try:
13 import _winreg as winreg
13 import _winreg as winreg # pytype: disable=import-error
14 14
15 15 winreg.CloseKey
16 16 except ImportError:
17 import winreg
17 # py2 only
18 import winreg # pytype: disable=import-error
18 19
19 20 # MS-DOS 'more' is the only pager available by default on Windows.
20 21 fallbackpager = b'more'
21 22
22 23
23 24 def systemrcpath():
24 25 '''return default os-specific hgrc search path'''
25 26 rcpath = []
26 27 filename = win32.executablepath()
27 28 # Use mercurial.ini found in directory with hg.exe
28 29 progrc = os.path.join(os.path.dirname(filename), b'mercurial.ini')
29 30 rcpath.append(progrc)
30 31 # Use hgrc.d found in directory with hg.exe
31 32 progrcd = os.path.join(os.path.dirname(filename), b'hgrc.d')
32 33 if os.path.isdir(progrcd):
33 34 for f, kind in util.listdir(progrcd):
34 35 if f.endswith(b'.rc'):
35 36 rcpath.append(os.path.join(progrcd, f))
36 37 # else look for a system rcpath in the registry
37 38 value = util.lookupreg(
38 39 b'SOFTWARE\\Mercurial', None, winreg.HKEY_LOCAL_MACHINE
39 40 )
40 41 if not isinstance(value, str) or not value:
41 42 return rcpath
42 43 value = util.localpath(value)
43 44 for p in value.split(pycompat.ospathsep):
44 45 if p.lower().endswith(b'mercurial.ini'):
45 46 rcpath.append(p)
46 47 elif os.path.isdir(p):
47 48 for f, kind in util.listdir(p):
48 49 if f.endswith(b'.rc'):
49 50 rcpath.append(os.path.join(p, f))
50 51 return rcpath
51 52
52 53
53 54 def userrcpath():
54 55 '''return os-specific hgrc search path to the user dir'''
55 56 home = os.path.expanduser(b'~')
56 57 path = [os.path.join(home, b'mercurial.ini'), os.path.join(home, b'.hgrc')]
57 58 userprofile = encoding.environ.get(b'USERPROFILE')
58 59 if userprofile and userprofile != home:
59 60 path.append(os.path.join(userprofile, b'mercurial.ini'))
60 61 path.append(os.path.join(userprofile, b'.hgrc'))
61 62 return path
62 63
63 64
64 65 def termsize(ui):
65 66 return win32.termsize()
@@ -1,689 +1,690 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 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 import _winreg as winreg
29 import _winreg as winreg # pytype: disable=import-error
30 30
31 31 winreg.CloseKey
32 32 except ImportError:
33 import winreg
33 # py2 only
34 import winreg # pytype: disable=import-error
34 35
35 36 osutil = policy.importmod('osutil')
36 37
37 38 getfsmountpoint = win32.getvolumename
38 39 getfstype = win32.getfstype
39 40 getuser = win32.getuser
40 41 hidewindow = win32.hidewindow
41 42 makedir = win32.makedir
42 43 nlinks = win32.nlinks
43 44 oslink = win32.oslink
44 45 samedevice = win32.samedevice
45 46 samefile = win32.samefile
46 47 setsignalhandler = win32.setsignalhandler
47 48 spawndetached = win32.spawndetached
48 49 split = os.path.split
49 50 testpid = win32.testpid
50 51 unlink = win32.unlink
51 52
52 53 umask = 0o022
53 54
54 55
55 56 class mixedfilemodewrapper(object):
56 57 """Wraps a file handle when it is opened in read/write mode.
57 58
58 59 fopen() and fdopen() on Windows have a specific-to-Windows requirement
59 60 that files opened with mode r+, w+, or a+ make a call to a file positioning
60 61 function when switching between reads and writes. Without this extra call,
61 62 Python will raise a not very intuitive "IOError: [Errno 0] Error."
62 63
63 64 This class wraps posixfile instances when the file is opened in read/write
64 65 mode and automatically adds checks or inserts appropriate file positioning
65 66 calls when necessary.
66 67 """
67 68
68 69 OPNONE = 0
69 70 OPREAD = 1
70 71 OPWRITE = 2
71 72
72 73 def __init__(self, fp):
73 74 object.__setattr__(self, '_fp', fp)
74 75 object.__setattr__(self, '_lastop', 0)
75 76
76 77 def __enter__(self):
77 78 self._fp.__enter__()
78 79 return self
79 80
80 81 def __exit__(self, exc_type, exc_val, exc_tb):
81 82 self._fp.__exit__(exc_type, exc_val, exc_tb)
82 83
83 84 def __getattr__(self, name):
84 85 return getattr(self._fp, name)
85 86
86 87 def __setattr__(self, name, value):
87 88 return self._fp.__setattr__(name, value)
88 89
89 90 def _noopseek(self):
90 91 self._fp.seek(0, os.SEEK_CUR)
91 92
92 93 def seek(self, *args, **kwargs):
93 94 object.__setattr__(self, '_lastop', self.OPNONE)
94 95 return self._fp.seek(*args, **kwargs)
95 96
96 97 def write(self, d):
97 98 if self._lastop == self.OPREAD:
98 99 self._noopseek()
99 100
100 101 object.__setattr__(self, '_lastop', self.OPWRITE)
101 102 return self._fp.write(d)
102 103
103 104 def writelines(self, *args, **kwargs):
104 105 if self._lastop == self.OPREAD:
105 106 self._noopeseek()
106 107
107 108 object.__setattr__(self, '_lastop', self.OPWRITE)
108 109 return self._fp.writelines(*args, **kwargs)
109 110
110 111 def read(self, *args, **kwargs):
111 112 if self._lastop == self.OPWRITE:
112 113 self._noopseek()
113 114
114 115 object.__setattr__(self, '_lastop', self.OPREAD)
115 116 return self._fp.read(*args, **kwargs)
116 117
117 118 def readline(self, *args, **kwargs):
118 119 if self._lastop == self.OPWRITE:
119 120 self._noopseek()
120 121
121 122 object.__setattr__(self, '_lastop', self.OPREAD)
122 123 return self._fp.readline(*args, **kwargs)
123 124
124 125 def readlines(self, *args, **kwargs):
125 126 if self._lastop == self.OPWRITE:
126 127 self._noopseek()
127 128
128 129 object.__setattr__(self, '_lastop', self.OPREAD)
129 130 return self._fp.readlines(*args, **kwargs)
130 131
131 132
132 133 class fdproxy(object):
133 134 """Wraps osutil.posixfile() to override the name attribute to reflect the
134 135 underlying file name.
135 136 """
136 137
137 138 def __init__(self, name, fp):
138 139 self.name = name
139 140 self._fp = fp
140 141
141 142 def __enter__(self):
142 143 self._fp.__enter__()
143 144 # Return this wrapper for the context manager so that the name is
144 145 # still available.
145 146 return self
146 147
147 148 def __exit__(self, exc_type, exc_value, traceback):
148 149 self._fp.__exit__(exc_type, exc_value, traceback)
149 150
150 151 def __iter__(self):
151 152 return iter(self._fp)
152 153
153 154 def __getattr__(self, name):
154 155 return getattr(self._fp, name)
155 156
156 157
157 158 def posixfile(name, mode=b'r', buffering=-1):
158 159 '''Open a file with even more POSIX-like semantics'''
159 160 try:
160 161 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
161 162
162 163 # PyFile_FromFd() ignores the name, and seems to report fp.name as the
163 164 # underlying file descriptor.
164 165 if pycompat.ispy3:
165 166 fp = fdproxy(name, fp)
166 167
167 168 # The position when opening in append mode is implementation defined, so
168 169 # make it consistent with other platforms, which position at EOF.
169 170 if b'a' in mode:
170 171 fp.seek(0, os.SEEK_END)
171 172
172 173 if b'+' in mode:
173 174 return mixedfilemodewrapper(fp)
174 175
175 176 return fp
176 177 except WindowsError as err:
177 178 # convert to a friendlier exception
178 179 raise IOError(
179 180 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
180 181 )
181 182
182 183
183 184 # may be wrapped by win32mbcs extension
184 185 listdir = osutil.listdir
185 186
186 187
187 188 class winstdout(object):
188 189 '''stdout on windows misbehaves if sent through a pipe'''
189 190
190 191 def __init__(self, fp):
191 192 self.fp = fp
192 193
193 194 def __getattr__(self, key):
194 195 return getattr(self.fp, key)
195 196
196 197 def close(self):
197 198 try:
198 199 self.fp.close()
199 200 except IOError:
200 201 pass
201 202
202 203 def write(self, s):
203 204 try:
204 205 # This is workaround for "Not enough space" error on
205 206 # writing large size of data to console.
206 207 limit = 16000
207 208 l = len(s)
208 209 start = 0
209 210 self.softspace = 0
210 211 while start < l:
211 212 end = start + limit
212 213 self.fp.write(s[start:end])
213 214 start = end
214 215 except IOError as inst:
215 216 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
216 217 raise
217 218 self.close()
218 219 raise IOError(errno.EPIPE, 'Broken pipe')
219 220
220 221 def flush(self):
221 222 try:
222 223 return self.fp.flush()
223 224 except IOError as inst:
224 225 if not win32.lasterrorwaspipeerror(inst):
225 226 raise
226 227 raise IOError(errno.EPIPE, 'Broken pipe')
227 228
228 229
229 230 def _is_win_9x():
230 231 '''return true if run on windows 95, 98 or me.'''
231 232 try:
232 233 return sys.getwindowsversion()[3] == 1
233 234 except AttributeError:
234 235 return b'command' in encoding.environ.get(b'comspec', b'')
235 236
236 237
237 238 def openhardlinks():
238 239 return not _is_win_9x()
239 240
240 241
241 242 def parsepatchoutput(output_line):
242 243 """parses the output produced by patch and returns the filename"""
243 244 pf = output_line[14:]
244 245 if pf[0] == b'`':
245 246 pf = pf[1:-1] # Remove the quotes
246 247 return pf
247 248
248 249
249 250 def sshargs(sshcmd, host, user, port):
250 251 '''Build argument list for ssh or Plink'''
251 252 pflag = b'plink' in sshcmd.lower() and b'-P' or b'-p'
252 253 args = user and (b"%s@%s" % (user, host)) or host
253 254 if args.startswith(b'-') or args.startswith(b'/'):
254 255 raise error.Abort(
255 256 _(b'illegal ssh hostname or username starting with - or /: %s')
256 257 % args
257 258 )
258 259 args = shellquote(args)
259 260 if port:
260 261 args = b'%s %s %s' % (pflag, shellquote(port), args)
261 262 return args
262 263
263 264
264 265 def setflags(f, l, x):
265 266 pass
266 267
267 268
268 269 def copymode(src, dst, mode=None, enforcewritable=False):
269 270 pass
270 271
271 272
272 273 def checkexec(path):
273 274 return False
274 275
275 276
276 277 def checklink(path):
277 278 return False
278 279
279 280
280 281 def setbinary(fd):
281 282 # When run without console, pipes may expose invalid
282 283 # fileno(), usually set to -1.
283 284 fno = getattr(fd, 'fileno', None)
284 285 if fno is not None and fno() >= 0:
285 msvcrt.setmode(fno(), os.O_BINARY)
286 msvcrt.setmode(fno(), os.O_BINARY) # pytype: disable=module-attr
286 287
287 288
288 289 def pconvert(path):
289 290 return path.replace(pycompat.ossep, b'/')
290 291
291 292
292 293 def localpath(path):
293 294 return path.replace(b'/', b'\\')
294 295
295 296
296 297 def normpath(path):
297 298 return pconvert(os.path.normpath(path))
298 299
299 300
300 301 def normcase(path):
301 302 return encoding.upper(path) # NTFS compares via upper()
302 303
303 304
304 305 # see posix.py for definitions
305 306 normcasespec = encoding.normcasespecs.upper
306 307 normcasefallback = encoding.upperfallback
307 308
308 309
309 310 def samestat(s1, s2):
310 311 return False
311 312
312 313
313 314 def shelltocmdexe(path, env):
314 315 r"""Convert shell variables in the form $var and ${var} inside ``path``
315 316 to %var% form. Existing Windows style variables are left unchanged.
316 317
317 318 The variables are limited to the given environment. Unknown variables are
318 319 left unchanged.
319 320
320 321 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
321 322 >>> # Only valid values are expanded
322 323 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
323 324 ... e)
324 325 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
325 326 >>> # Single quote prevents expansion, as does \$ escaping
326 327 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
327 328 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
328 329 >>> # $$ is not special. %% is not special either, but can be the end and
329 330 >>> # start of consecutive variables
330 331 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
331 332 'cmd $$ %% %var1%%var2%'
332 333 >>> # No double substitution
333 334 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
334 335 '%var1% %var1%'
335 336 >>> # Tilde expansion
336 337 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
337 338 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
338 339 """
339 340 if not any(c in path for c in b"$'~"):
340 341 return path
341 342
342 343 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
343 344
344 345 res = b''
345 346 index = 0
346 347 pathlen = len(path)
347 348 while index < pathlen:
348 349 c = path[index : index + 1]
349 350 if c == b'\'': # no expansion within single quotes
350 351 path = path[index + 1 :]
351 352 pathlen = len(path)
352 353 try:
353 354 index = path.index(b'\'')
354 355 res += b'"' + path[:index] + b'"'
355 356 except ValueError:
356 357 res += c + path
357 358 index = pathlen - 1
358 359 elif c == b'%': # variable
359 360 path = path[index + 1 :]
360 361 pathlen = len(path)
361 362 try:
362 363 index = path.index(b'%')
363 364 except ValueError:
364 365 res += b'%' + path
365 366 index = pathlen - 1
366 367 else:
367 368 var = path[:index]
368 369 res += b'%' + var + b'%'
369 370 elif c == b'$': # variable
370 371 if path[index + 1 : index + 2] == b'{':
371 372 path = path[index + 2 :]
372 373 pathlen = len(path)
373 374 try:
374 375 index = path.index(b'}')
375 376 var = path[:index]
376 377
377 378 # See below for why empty variables are handled specially
378 379 if env.get(var, b'') != b'':
379 380 res += b'%' + var + b'%'
380 381 else:
381 382 res += b'${' + var + b'}'
382 383 except ValueError:
383 384 res += b'${' + path
384 385 index = pathlen - 1
385 386 else:
386 387 var = b''
387 388 index += 1
388 389 c = path[index : index + 1]
389 390 while c != b'' and c in varchars:
390 391 var += c
391 392 index += 1
392 393 c = path[index : index + 1]
393 394 # Some variables (like HG_OLDNODE) may be defined, but have an
394 395 # empty value. Those need to be skipped because when spawning
395 396 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
396 397 # VAR, and that really confuses things like revset expressions.
397 398 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
398 399 # will substitute to an empty string, and everything is happy.
399 400 if env.get(var, b'') != b'':
400 401 res += b'%' + var + b'%'
401 402 else:
402 403 res += b'$' + var
403 404
404 405 if c != b'':
405 406 index -= 1
406 407 elif (
407 408 c == b'~'
408 409 and index + 1 < pathlen
409 410 and path[index + 1 : index + 2] in (b'\\', b'/')
410 411 ):
411 412 res += b"%USERPROFILE%"
412 413 elif (
413 414 c == b'\\'
414 415 and index + 1 < pathlen
415 416 and path[index + 1 : index + 2] in (b'$', b'~')
416 417 ):
417 418 # Skip '\', but only if it is escaping $ or ~
418 419 res += path[index + 1 : index + 2]
419 420 index += 1
420 421 else:
421 422 res += c
422 423
423 424 index += 1
424 425 return res
425 426
426 427
427 428 # A sequence of backslashes is special iff it precedes a double quote:
428 429 # - if there's an even number of backslashes, the double quote is not
429 430 # quoted (i.e. it ends the quoted region)
430 431 # - if there's an odd number of backslashes, the double quote is quoted
431 432 # - in both cases, every pair of backslashes is unquoted into a single
432 433 # backslash
433 434 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
434 435 # So, to quote a string, we must surround it in double quotes, double
435 436 # the number of backslashes that precede double quotes and add another
436 437 # backslash before every double quote (being careful with the double
437 438 # quote we've appended to the end)
438 439 _quotere = None
439 440 _needsshellquote = None
440 441
441 442
442 443 def shellquote(s):
443 444 r"""
444 445 >>> shellquote(br'C:\Users\xyz')
445 446 '"C:\\Users\\xyz"'
446 447 >>> shellquote(br'C:\Users\xyz/mixed')
447 448 '"C:\\Users\\xyz/mixed"'
448 449 >>> # Would be safe not to quote too, since it is all double backslashes
449 450 >>> shellquote(br'C:\\Users\\xyz')
450 451 '"C:\\\\Users\\\\xyz"'
451 452 >>> # But this must be quoted
452 453 >>> shellquote(br'C:\\Users\\xyz/abc')
453 454 '"C:\\\\Users\\\\xyz/abc"'
454 455 """
455 456 global _quotere
456 457 if _quotere is None:
457 458 _quotere = re.compile(br'(\\*)("|\\$)')
458 459 global _needsshellquote
459 460 if _needsshellquote is None:
460 461 # ":" is also treated as "safe character", because it is used as a part
461 462 # of path name on Windows. "\" is also part of a path name, but isn't
462 463 # safe because shlex.split() (kind of) treats it as an escape char and
463 464 # drops it. It will leave the next character, even if it is another
464 465 # "\".
465 466 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
466 467 if s and not _needsshellquote(s) and not _quotere.search(s):
467 468 # "s" shouldn't have to be quoted
468 469 return s
469 470 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
470 471
471 472
472 473 def _unquote(s):
473 474 if s.startswith(b'"') and s.endswith(b'"'):
474 475 return s[1:-1]
475 476 return s
476 477
477 478
478 479 def shellsplit(s):
479 480 """Parse a command string in cmd.exe way (best-effort)"""
480 481 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
481 482
482 483
483 484 def quotecommand(cmd):
484 485 """Build a command string suitable for os.popen* calls."""
485 486 if sys.version_info < (2, 7, 1):
486 487 # Python versions since 2.7.1 do this extra quoting themselves
487 488 return b'"' + cmd + b'"'
488 489 return cmd
489 490
490 491
491 492 # if you change this stub into a real check, please try to implement the
492 493 # username and groupname functions above, too.
493 494 def isowner(st):
494 495 return True
495 496
496 497
497 498 def findexe(command):
498 499 '''Find executable for command searching like cmd.exe does.
499 500 If command is a basename then PATH is searched for command.
500 501 PATH isn't searched if command is an absolute or relative path.
501 502 An extension from PATHEXT is found and added if not present.
502 503 If command isn't found None is returned.'''
503 504 pathext = encoding.environ.get(b'PATHEXT', b'.COM;.EXE;.BAT;.CMD')
504 505 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
505 506 if os.path.splitext(command)[1].lower() in pathexts:
506 507 pathexts = [b'']
507 508
508 509 def findexisting(pathcommand):
509 510 b'Will append extension (if needed) and return existing file'
510 511 for ext in pathexts:
511 512 executable = pathcommand + ext
512 513 if os.path.exists(executable):
513 514 return executable
514 515 return None
515 516
516 517 if pycompat.ossep in command:
517 518 return findexisting(command)
518 519
519 520 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
520 521 executable = findexisting(os.path.join(path, command))
521 522 if executable is not None:
522 523 return executable
523 524 return findexisting(os.path.expanduser(os.path.expandvars(command)))
524 525
525 526
526 527 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
527 528
528 529
529 530 def statfiles(files):
530 531 '''Stat each file in files. Yield each stat, or None if a file
531 532 does not exist or has a type we don't care about.
532 533
533 534 Cluster and cache stat per directory to minimize number of OS stat calls.'''
534 535 dircache = {} # dirname -> filename -> status | None if file does not exist
535 536 getkind = stat.S_IFMT
536 537 for nf in files:
537 538 nf = normcase(nf)
538 539 dir, base = os.path.split(nf)
539 540 if not dir:
540 541 dir = b'.'
541 542 cache = dircache.get(dir, None)
542 543 if cache is None:
543 544 try:
544 545 dmap = dict(
545 546 [
546 547 (normcase(n), s)
547 548 for n, k, s in listdir(dir, True)
548 549 if getkind(s.st_mode) in _wantedkinds
549 550 ]
550 551 )
551 552 except OSError as err:
552 553 # Python >= 2.5 returns ENOENT and adds winerror field
553 554 # EINVAL is raised if dir is not a directory.
554 555 if err.errno not in (errno.ENOENT, errno.EINVAL, errno.ENOTDIR):
555 556 raise
556 557 dmap = {}
557 558 cache = dircache.setdefault(dir, dmap)
558 559 yield cache.get(base, None)
559 560
560 561
561 562 def username(uid=None):
562 563 """Return the name of the user with the given uid.
563 564
564 565 If uid is None, return the name of the current user."""
565 566 return None
566 567
567 568
568 569 def groupname(gid=None):
569 570 """Return the name of the group with the given gid.
570 571
571 572 If gid is None, return the name of the current group."""
572 573 return None
573 574
574 575
575 576 def readlink(pathname):
576 577 return pycompat.fsencode(os.readlink(pycompat.fsdecode(pathname)))
577 578
578 579
579 580 def removedirs(name):
580 581 """special version of os.removedirs that does not remove symlinked
581 582 directories or junction points if they actually contain files"""
582 583 if listdir(name):
583 584 return
584 585 os.rmdir(name)
585 586 head, tail = os.path.split(name)
586 587 if not tail:
587 588 head, tail = os.path.split(head)
588 589 while head and tail:
589 590 try:
590 591 if listdir(head):
591 592 return
592 593 os.rmdir(head)
593 594 except (ValueError, OSError):
594 595 break
595 596 head, tail = os.path.split(head)
596 597
597 598
598 599 def rename(src, dst):
599 600 '''atomically rename file src to dst, replacing dst if it exists'''
600 601 try:
601 602 os.rename(src, dst)
602 603 except OSError as e:
603 604 if e.errno != errno.EEXIST:
604 605 raise
605 606 unlink(dst)
606 607 os.rename(src, dst)
607 608
608 609
609 610 def gethgcmd():
610 611 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
611 612
612 613
613 614 def groupmembers(name):
614 615 # Don't support groups on Windows for now
615 616 raise KeyError
616 617
617 618
618 619 def isexec(f):
619 620 return False
620 621
621 622
622 623 class cachestat(object):
623 624 def __init__(self, path):
624 625 pass
625 626
626 627 def cacheable(self):
627 628 return False
628 629
629 630
630 631 def lookupreg(key, valname=None, scope=None):
631 632 ''' Look up a key/value name in the Windows registry.
632 633
633 634 valname: value name. If unspecified, the default value for the key
634 635 is used.
635 636 scope: optionally specify scope for registry lookup, this can be
636 637 a sequence of scopes to look up in order. Default (CURRENT_USER,
637 638 LOCAL_MACHINE).
638 639 '''
639 640 if scope is None:
640 641 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
641 642 elif not isinstance(scope, (list, tuple)):
642 643 scope = (scope,)
643 644 for s in scope:
644 645 try:
645 646 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
646 647 name = valname and encoding.strfromlocal(valname) or valname
647 648 val = winreg.QueryValueEx(hkey, name)[0]
648 649 # never let a Unicode string escape into the wild
649 650 return encoding.unitolocal(val)
650 651 except EnvironmentError:
651 652 pass
652 653
653 654
654 655 expandglobs = True
655 656
656 657
657 658 def statislink(st):
658 659 '''check whether a stat result is a symlink'''
659 660 return False
660 661
661 662
662 663 def statisexec(st):
663 664 '''check whether a stat result is an executable file'''
664 665 return False
665 666
666 667
667 668 def poll(fds):
668 669 # see posix.py for description
669 670 raise NotImplementedError()
670 671
671 672
672 673 def readpipe(pipe):
673 674 """Read all available data from a pipe."""
674 675 chunks = []
675 676 while True:
676 677 size = win32.peekpipe(pipe)
677 678 if not size:
678 679 break
679 680
680 681 s = pipe.read(size)
681 682 if not s:
682 683 break
683 684 chunks.append(s)
684 685
685 686 return b''.join(chunks)
686 687
687 688
688 689 def bindunixsocket(sock, path):
689 690 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now