##// END OF EJS Templates
util: replace file I/O with readfile
Bryan O'Sullivan -
r27768:5ef99738 default
parent child Browse files
Show More
@@ -1,2728 +1,2725
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
17 17
18 18 import bz2
19 19 import calendar
20 20 import collections
21 21 import datetime
22 22 import errno
23 23 import gc
24 24 import hashlib
25 25 import imp
26 26 import os
27 27 import re as remod
28 28 import shutil
29 29 import signal
30 30 import socket
31 31 import subprocess
32 32 import sys
33 33 import tempfile
34 34 import textwrap
35 35 import time
36 36 import traceback
37 37 import urllib
38 38 import zlib
39 39
40 40 from . import (
41 41 encoding,
42 42 error,
43 43 i18n,
44 44 osutil,
45 45 parsers,
46 46 )
47 47
48 48 if os.name == 'nt':
49 49 from . import windows as platform
50 50 else:
51 51 from . import posix as platform
52 52
53 53 md5 = hashlib.md5
54 54 sha1 = hashlib.sha1
55 55 sha512 = hashlib.sha512
56 56 _ = i18n._
57 57
58 58 cachestat = platform.cachestat
59 59 checkexec = platform.checkexec
60 60 checklink = platform.checklink
61 61 copymode = platform.copymode
62 62 executablepath = platform.executablepath
63 63 expandglobs = platform.expandglobs
64 64 explainexit = platform.explainexit
65 65 findexe = platform.findexe
66 66 gethgcmd = platform.gethgcmd
67 67 getuser = platform.getuser
68 68 groupmembers = platform.groupmembers
69 69 groupname = platform.groupname
70 70 hidewindow = platform.hidewindow
71 71 isexec = platform.isexec
72 72 isowner = platform.isowner
73 73 localpath = platform.localpath
74 74 lookupreg = platform.lookupreg
75 75 makedir = platform.makedir
76 76 nlinks = platform.nlinks
77 77 normpath = platform.normpath
78 78 normcase = platform.normcase
79 79 normcasespec = platform.normcasespec
80 80 normcasefallback = platform.normcasefallback
81 81 openhardlinks = platform.openhardlinks
82 82 oslink = platform.oslink
83 83 parsepatchoutput = platform.parsepatchoutput
84 84 pconvert = platform.pconvert
85 85 poll = platform.poll
86 86 popen = platform.popen
87 87 posixfile = platform.posixfile
88 88 quotecommand = platform.quotecommand
89 89 readpipe = platform.readpipe
90 90 rename = platform.rename
91 91 removedirs = platform.removedirs
92 92 samedevice = platform.samedevice
93 93 samefile = platform.samefile
94 94 samestat = platform.samestat
95 95 setbinary = platform.setbinary
96 96 setflags = platform.setflags
97 97 setsignalhandler = platform.setsignalhandler
98 98 shellquote = platform.shellquote
99 99 spawndetached = platform.spawndetached
100 100 split = platform.split
101 101 sshargs = platform.sshargs
102 102 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
103 103 statisexec = platform.statisexec
104 104 statislink = platform.statislink
105 105 termwidth = platform.termwidth
106 106 testpid = platform.testpid
107 107 umask = platform.umask
108 108 unlink = platform.unlink
109 109 unlinkpath = platform.unlinkpath
110 110 username = platform.username
111 111
112 112 # Python compatibility
113 113
114 114 _notset = object()
115 115
116 116 # disable Python's problematic floating point timestamps (issue4836)
117 117 # (Python hypocritically says you shouldn't change this behavior in
118 118 # libraries, and sure enough Mercurial is not a library.)
119 119 os.stat_float_times(False)
120 120
121 121 def safehasattr(thing, attr):
122 122 return getattr(thing, attr, _notset) is not _notset
123 123
124 124 DIGESTS = {
125 125 'md5': md5,
126 126 'sha1': sha1,
127 127 'sha512': sha512,
128 128 }
129 129 # List of digest types from strongest to weakest
130 130 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
131 131
132 132 for k in DIGESTS_BY_STRENGTH:
133 133 assert k in DIGESTS
134 134
135 135 class digester(object):
136 136 """helper to compute digests.
137 137
138 138 This helper can be used to compute one or more digests given their name.
139 139
140 140 >>> d = digester(['md5', 'sha1'])
141 141 >>> d.update('foo')
142 142 >>> [k for k in sorted(d)]
143 143 ['md5', 'sha1']
144 144 >>> d['md5']
145 145 'acbd18db4cc2f85cedef654fccc4a4d8'
146 146 >>> d['sha1']
147 147 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
148 148 >>> digester.preferred(['md5', 'sha1'])
149 149 'sha1'
150 150 """
151 151
152 152 def __init__(self, digests, s=''):
153 153 self._hashes = {}
154 154 for k in digests:
155 155 if k not in DIGESTS:
156 156 raise Abort(_('unknown digest type: %s') % k)
157 157 self._hashes[k] = DIGESTS[k]()
158 158 if s:
159 159 self.update(s)
160 160
161 161 def update(self, data):
162 162 for h in self._hashes.values():
163 163 h.update(data)
164 164
165 165 def __getitem__(self, key):
166 166 if key not in DIGESTS:
167 167 raise Abort(_('unknown digest type: %s') % k)
168 168 return self._hashes[key].hexdigest()
169 169
170 170 def __iter__(self):
171 171 return iter(self._hashes)
172 172
173 173 @staticmethod
174 174 def preferred(supported):
175 175 """returns the strongest digest type in both supported and DIGESTS."""
176 176
177 177 for k in DIGESTS_BY_STRENGTH:
178 178 if k in supported:
179 179 return k
180 180 return None
181 181
182 182 class digestchecker(object):
183 183 """file handle wrapper that additionally checks content against a given
184 184 size and digests.
185 185
186 186 d = digestchecker(fh, size, {'md5': '...'})
187 187
188 188 When multiple digests are given, all of them are validated.
189 189 """
190 190
191 191 def __init__(self, fh, size, digests):
192 192 self._fh = fh
193 193 self._size = size
194 194 self._got = 0
195 195 self._digests = dict(digests)
196 196 self._digester = digester(self._digests.keys())
197 197
198 198 def read(self, length=-1):
199 199 content = self._fh.read(length)
200 200 self._digester.update(content)
201 201 self._got += len(content)
202 202 return content
203 203
204 204 def validate(self):
205 205 if self._size != self._got:
206 206 raise Abort(_('size mismatch: expected %d, got %d') %
207 207 (self._size, self._got))
208 208 for k, v in self._digests.items():
209 209 if v != self._digester[k]:
210 210 # i18n: first parameter is a digest name
211 211 raise Abort(_('%s mismatch: expected %s, got %s') %
212 212 (k, v, self._digester[k]))
213 213
214 214 try:
215 215 buffer = buffer
216 216 except NameError:
217 217 if sys.version_info[0] < 3:
218 218 def buffer(sliceable, offset=0):
219 219 return sliceable[offset:]
220 220 else:
221 221 def buffer(sliceable, offset=0):
222 222 return memoryview(sliceable)[offset:]
223 223
224 224 closefds = os.name == 'posix'
225 225
226 226 _chunksize = 4096
227 227
228 228 class bufferedinputpipe(object):
229 229 """a manually buffered input pipe
230 230
231 231 Python will not let us use buffered IO and lazy reading with 'polling' at
232 232 the same time. We cannot probe the buffer state and select will not detect
233 233 that data are ready to read if they are already buffered.
234 234
235 235 This class let us work around that by implementing its own buffering
236 236 (allowing efficient readline) while offering a way to know if the buffer is
237 237 empty from the output (allowing collaboration of the buffer with polling).
238 238
239 239 This class lives in the 'util' module because it makes use of the 'os'
240 240 module from the python stdlib.
241 241 """
242 242
243 243 def __init__(self, input):
244 244 self._input = input
245 245 self._buffer = []
246 246 self._eof = False
247 247 self._lenbuf = 0
248 248
249 249 @property
250 250 def hasbuffer(self):
251 251 """True is any data is currently buffered
252 252
253 253 This will be used externally a pre-step for polling IO. If there is
254 254 already data then no polling should be set in place."""
255 255 return bool(self._buffer)
256 256
257 257 @property
258 258 def closed(self):
259 259 return self._input.closed
260 260
261 261 def fileno(self):
262 262 return self._input.fileno()
263 263
264 264 def close(self):
265 265 return self._input.close()
266 266
267 267 def read(self, size):
268 268 while (not self._eof) and (self._lenbuf < size):
269 269 self._fillbuffer()
270 270 return self._frombuffer(size)
271 271
272 272 def readline(self, *args, **kwargs):
273 273 if 1 < len(self._buffer):
274 274 # this should not happen because both read and readline end with a
275 275 # _frombuffer call that collapse it.
276 276 self._buffer = [''.join(self._buffer)]
277 277 self._lenbuf = len(self._buffer[0])
278 278 lfi = -1
279 279 if self._buffer:
280 280 lfi = self._buffer[-1].find('\n')
281 281 while (not self._eof) and lfi < 0:
282 282 self._fillbuffer()
283 283 if self._buffer:
284 284 lfi = self._buffer[-1].find('\n')
285 285 size = lfi + 1
286 286 if lfi < 0: # end of file
287 287 size = self._lenbuf
288 288 elif 1 < len(self._buffer):
289 289 # we need to take previous chunks into account
290 290 size += self._lenbuf - len(self._buffer[-1])
291 291 return self._frombuffer(size)
292 292
293 293 def _frombuffer(self, size):
294 294 """return at most 'size' data from the buffer
295 295
296 296 The data are removed from the buffer."""
297 297 if size == 0 or not self._buffer:
298 298 return ''
299 299 buf = self._buffer[0]
300 300 if 1 < len(self._buffer):
301 301 buf = ''.join(self._buffer)
302 302
303 303 data = buf[:size]
304 304 buf = buf[len(data):]
305 305 if buf:
306 306 self._buffer = [buf]
307 307 self._lenbuf = len(buf)
308 308 else:
309 309 self._buffer = []
310 310 self._lenbuf = 0
311 311 return data
312 312
313 313 def _fillbuffer(self):
314 314 """read data to the buffer"""
315 315 data = os.read(self._input.fileno(), _chunksize)
316 316 if not data:
317 317 self._eof = True
318 318 else:
319 319 self._lenbuf += len(data)
320 320 self._buffer.append(data)
321 321
322 322 def popen2(cmd, env=None, newlines=False):
323 323 # Setting bufsize to -1 lets the system decide the buffer size.
324 324 # The default for bufsize is 0, meaning unbuffered. This leads to
325 325 # poor performance on Mac OS X: http://bugs.python.org/issue4194
326 326 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
327 327 close_fds=closefds,
328 328 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
329 329 universal_newlines=newlines,
330 330 env=env)
331 331 return p.stdin, p.stdout
332 332
333 333 def popen3(cmd, env=None, newlines=False):
334 334 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
335 335 return stdin, stdout, stderr
336 336
337 337 def popen4(cmd, env=None, newlines=False, bufsize=-1):
338 338 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
339 339 close_fds=closefds,
340 340 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
341 341 stderr=subprocess.PIPE,
342 342 universal_newlines=newlines,
343 343 env=env)
344 344 return p.stdin, p.stdout, p.stderr, p
345 345
346 346 def version():
347 347 """Return version information if available."""
348 348 try:
349 349 from . import __version__
350 350 return __version__.version
351 351 except ImportError:
352 352 return 'unknown'
353 353
354 354 def versiontuple(v=None, n=4):
355 355 """Parses a Mercurial version string into an N-tuple.
356 356
357 357 The version string to be parsed is specified with the ``v`` argument.
358 358 If it isn't defined, the current Mercurial version string will be parsed.
359 359
360 360 ``n`` can be 2, 3, or 4. Here is how some version strings map to
361 361 returned values:
362 362
363 363 >>> v = '3.6.1+190-df9b73d2d444'
364 364 >>> versiontuple(v, 2)
365 365 (3, 6)
366 366 >>> versiontuple(v, 3)
367 367 (3, 6, 1)
368 368 >>> versiontuple(v, 4)
369 369 (3, 6, 1, '190-df9b73d2d444')
370 370
371 371 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
372 372 (3, 6, 1, '190-df9b73d2d444+20151118')
373 373
374 374 >>> v = '3.6'
375 375 >>> versiontuple(v, 2)
376 376 (3, 6)
377 377 >>> versiontuple(v, 3)
378 378 (3, 6, None)
379 379 >>> versiontuple(v, 4)
380 380 (3, 6, None, None)
381 381 """
382 382 if not v:
383 383 v = version()
384 384 parts = v.split('+', 1)
385 385 if len(parts) == 1:
386 386 vparts, extra = parts[0], None
387 387 else:
388 388 vparts, extra = parts
389 389
390 390 vints = []
391 391 for i in vparts.split('.'):
392 392 try:
393 393 vints.append(int(i))
394 394 except ValueError:
395 395 break
396 396 # (3, 6) -> (3, 6, None)
397 397 while len(vints) < 3:
398 398 vints.append(None)
399 399
400 400 if n == 2:
401 401 return (vints[0], vints[1])
402 402 if n == 3:
403 403 return (vints[0], vints[1], vints[2])
404 404 if n == 4:
405 405 return (vints[0], vints[1], vints[2], extra)
406 406
407 407 # used by parsedate
408 408 defaultdateformats = (
409 409 '%Y-%m-%d %H:%M:%S',
410 410 '%Y-%m-%d %I:%M:%S%p',
411 411 '%Y-%m-%d %H:%M',
412 412 '%Y-%m-%d %I:%M%p',
413 413 '%Y-%m-%d',
414 414 '%m-%d',
415 415 '%m/%d',
416 416 '%m/%d/%y',
417 417 '%m/%d/%Y',
418 418 '%a %b %d %H:%M:%S %Y',
419 419 '%a %b %d %I:%M:%S%p %Y',
420 420 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
421 421 '%b %d %H:%M:%S %Y',
422 422 '%b %d %I:%M:%S%p %Y',
423 423 '%b %d %H:%M:%S',
424 424 '%b %d %I:%M:%S%p',
425 425 '%b %d %H:%M',
426 426 '%b %d %I:%M%p',
427 427 '%b %d %Y',
428 428 '%b %d',
429 429 '%H:%M:%S',
430 430 '%I:%M:%S%p',
431 431 '%H:%M',
432 432 '%I:%M%p',
433 433 )
434 434
435 435 extendeddateformats = defaultdateformats + (
436 436 "%Y",
437 437 "%Y-%m",
438 438 "%b",
439 439 "%b %Y",
440 440 )
441 441
442 442 def cachefunc(func):
443 443 '''cache the result of function calls'''
444 444 # XXX doesn't handle keywords args
445 445 if func.func_code.co_argcount == 0:
446 446 cache = []
447 447 def f():
448 448 if len(cache) == 0:
449 449 cache.append(func())
450 450 return cache[0]
451 451 return f
452 452 cache = {}
453 453 if func.func_code.co_argcount == 1:
454 454 # we gain a small amount of time because
455 455 # we don't need to pack/unpack the list
456 456 def f(arg):
457 457 if arg not in cache:
458 458 cache[arg] = func(arg)
459 459 return cache[arg]
460 460 else:
461 461 def f(*args):
462 462 if args not in cache:
463 463 cache[args] = func(*args)
464 464 return cache[args]
465 465
466 466 return f
467 467
468 468 class sortdict(dict):
469 469 '''a simple sorted dictionary'''
470 470 def __init__(self, data=None):
471 471 self._list = []
472 472 if data:
473 473 self.update(data)
474 474 def copy(self):
475 475 return sortdict(self)
476 476 def __setitem__(self, key, val):
477 477 if key in self:
478 478 self._list.remove(key)
479 479 self._list.append(key)
480 480 dict.__setitem__(self, key, val)
481 481 def __iter__(self):
482 482 return self._list.__iter__()
483 483 def update(self, src):
484 484 if isinstance(src, dict):
485 485 src = src.iteritems()
486 486 for k, v in src:
487 487 self[k] = v
488 488 def clear(self):
489 489 dict.clear(self)
490 490 self._list = []
491 491 def items(self):
492 492 return [(k, self[k]) for k in self._list]
493 493 def __delitem__(self, key):
494 494 dict.__delitem__(self, key)
495 495 self._list.remove(key)
496 496 def pop(self, key, *args, **kwargs):
497 497 dict.pop(self, key, *args, **kwargs)
498 498 try:
499 499 self._list.remove(key)
500 500 except ValueError:
501 501 pass
502 502 def keys(self):
503 503 return self._list
504 504 def iterkeys(self):
505 505 return self._list.__iter__()
506 506 def iteritems(self):
507 507 for k in self._list:
508 508 yield k, self[k]
509 509 def insert(self, index, key, val):
510 510 self._list.insert(index, key)
511 511 dict.__setitem__(self, key, val)
512 512
513 513 class _lrucachenode(object):
514 514 """A node in a doubly linked list.
515 515
516 516 Holds a reference to nodes on either side as well as a key-value
517 517 pair for the dictionary entry.
518 518 """
519 519 __slots__ = ('next', 'prev', 'key', 'value')
520 520
521 521 def __init__(self):
522 522 self.next = None
523 523 self.prev = None
524 524
525 525 self.key = _notset
526 526 self.value = None
527 527
528 528 def markempty(self):
529 529 """Mark the node as emptied."""
530 530 self.key = _notset
531 531
532 532 class lrucachedict(object):
533 533 """Dict that caches most recent accesses and sets.
534 534
535 535 The dict consists of an actual backing dict - indexed by original
536 536 key - and a doubly linked circular list defining the order of entries in
537 537 the cache.
538 538
539 539 The head node is the newest entry in the cache. If the cache is full,
540 540 we recycle head.prev and make it the new head. Cache accesses result in
541 541 the node being moved to before the existing head and being marked as the
542 542 new head node.
543 543 """
544 544 def __init__(self, max):
545 545 self._cache = {}
546 546
547 547 self._head = head = _lrucachenode()
548 548 head.prev = head
549 549 head.next = head
550 550 self._size = 1
551 551 self._capacity = max
552 552
553 553 def __len__(self):
554 554 return len(self._cache)
555 555
556 556 def __contains__(self, k):
557 557 return k in self._cache
558 558
559 559 def __iter__(self):
560 560 # We don't have to iterate in cache order, but why not.
561 561 n = self._head
562 562 for i in range(len(self._cache)):
563 563 yield n.key
564 564 n = n.next
565 565
566 566 def __getitem__(self, k):
567 567 node = self._cache[k]
568 568 self._movetohead(node)
569 569 return node.value
570 570
571 571 def __setitem__(self, k, v):
572 572 node = self._cache.get(k)
573 573 # Replace existing value and mark as newest.
574 574 if node is not None:
575 575 node.value = v
576 576 self._movetohead(node)
577 577 return
578 578
579 579 if self._size < self._capacity:
580 580 node = self._addcapacity()
581 581 else:
582 582 # Grab the last/oldest item.
583 583 node = self._head.prev
584 584
585 585 # At capacity. Kill the old entry.
586 586 if node.key is not _notset:
587 587 del self._cache[node.key]
588 588
589 589 node.key = k
590 590 node.value = v
591 591 self._cache[k] = node
592 592 # And mark it as newest entry. No need to adjust order since it
593 593 # is already self._head.prev.
594 594 self._head = node
595 595
596 596 def __delitem__(self, k):
597 597 node = self._cache.pop(k)
598 598 node.markempty()
599 599
600 600 # Temporarily mark as newest item before re-adjusting head to make
601 601 # this node the oldest item.
602 602 self._movetohead(node)
603 603 self._head = node.next
604 604
605 605 # Additional dict methods.
606 606
607 607 def get(self, k, default=None):
608 608 try:
609 609 return self._cache[k]
610 610 except KeyError:
611 611 return default
612 612
613 613 def clear(self):
614 614 n = self._head
615 615 while n.key is not _notset:
616 616 n.markempty()
617 617 n = n.next
618 618
619 619 self._cache.clear()
620 620
621 621 def copy(self):
622 622 result = lrucachedict(self._capacity)
623 623 n = self._head.prev
624 624 # Iterate in oldest-to-newest order, so the copy has the right ordering
625 625 for i in range(len(self._cache)):
626 626 result[n.key] = n.value
627 627 n = n.prev
628 628 return result
629 629
630 630 def _movetohead(self, node):
631 631 """Mark a node as the newest, making it the new head.
632 632
633 633 When a node is accessed, it becomes the freshest entry in the LRU
634 634 list, which is denoted by self._head.
635 635
636 636 Visually, let's make ``N`` the new head node (* denotes head):
637 637
638 638 previous/oldest <-> head <-> next/next newest
639 639
640 640 ----<->--- A* ---<->-----
641 641 | |
642 642 E <-> D <-> N <-> C <-> B
643 643
644 644 To:
645 645
646 646 ----<->--- N* ---<->-----
647 647 | |
648 648 E <-> D <-> C <-> B <-> A
649 649
650 650 This requires the following moves:
651 651
652 652 C.next = D (node.prev.next = node.next)
653 653 D.prev = C (node.next.prev = node.prev)
654 654 E.next = N (head.prev.next = node)
655 655 N.prev = E (node.prev = head.prev)
656 656 N.next = A (node.next = head)
657 657 A.prev = N (head.prev = node)
658 658 """
659 659 head = self._head
660 660 # C.next = D
661 661 node.prev.next = node.next
662 662 # D.prev = C
663 663 node.next.prev = node.prev
664 664 # N.prev = E
665 665 node.prev = head.prev
666 666 # N.next = A
667 667 # It is tempting to do just "head" here, however if node is
668 668 # adjacent to head, this will do bad things.
669 669 node.next = head.prev.next
670 670 # E.next = N
671 671 node.next.prev = node
672 672 # A.prev = N
673 673 node.prev.next = node
674 674
675 675 self._head = node
676 676
677 677 def _addcapacity(self):
678 678 """Add a node to the circular linked list.
679 679
680 680 The new node is inserted before the head node.
681 681 """
682 682 head = self._head
683 683 node = _lrucachenode()
684 684 head.prev.next = node
685 685 node.prev = head.prev
686 686 node.next = head
687 687 head.prev = node
688 688 self._size += 1
689 689 return node
690 690
691 691 def lrucachefunc(func):
692 692 '''cache most recent results of function calls'''
693 693 cache = {}
694 694 order = collections.deque()
695 695 if func.func_code.co_argcount == 1:
696 696 def f(arg):
697 697 if arg not in cache:
698 698 if len(cache) > 20:
699 699 del cache[order.popleft()]
700 700 cache[arg] = func(arg)
701 701 else:
702 702 order.remove(arg)
703 703 order.append(arg)
704 704 return cache[arg]
705 705 else:
706 706 def f(*args):
707 707 if args not in cache:
708 708 if len(cache) > 20:
709 709 del cache[order.popleft()]
710 710 cache[args] = func(*args)
711 711 else:
712 712 order.remove(args)
713 713 order.append(args)
714 714 return cache[args]
715 715
716 716 return f
717 717
718 718 class propertycache(object):
719 719 def __init__(self, func):
720 720 self.func = func
721 721 self.name = func.__name__
722 722 def __get__(self, obj, type=None):
723 723 result = self.func(obj)
724 724 self.cachevalue(obj, result)
725 725 return result
726 726
727 727 def cachevalue(self, obj, value):
728 728 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
729 729 obj.__dict__[self.name] = value
730 730
731 731 def pipefilter(s, cmd):
732 732 '''filter string S through command CMD, returning its output'''
733 733 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
734 734 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
735 735 pout, perr = p.communicate(s)
736 736 return pout
737 737
738 738 def tempfilter(s, cmd):
739 739 '''filter string S through a pair of temporary files with CMD.
740 740 CMD is used as a template to create the real command to be run,
741 741 with the strings INFILE and OUTFILE replaced by the real names of
742 742 the temporary files generated.'''
743 743 inname, outname = None, None
744 744 try:
745 745 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
746 746 fp = os.fdopen(infd, 'wb')
747 747 fp.write(s)
748 748 fp.close()
749 749 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
750 750 os.close(outfd)
751 751 cmd = cmd.replace('INFILE', inname)
752 752 cmd = cmd.replace('OUTFILE', outname)
753 753 code = os.system(cmd)
754 754 if sys.platform == 'OpenVMS' and code & 1:
755 755 code = 0
756 756 if code:
757 757 raise Abort(_("command '%s' failed: %s") %
758 758 (cmd, explainexit(code)))
759 fp = open(outname, 'rb')
760 r = fp.read()
761 fp.close()
762 return r
759 return readfile(outname)
763 760 finally:
764 761 try:
765 762 if inname:
766 763 os.unlink(inname)
767 764 except OSError:
768 765 pass
769 766 try:
770 767 if outname:
771 768 os.unlink(outname)
772 769 except OSError:
773 770 pass
774 771
775 772 filtertable = {
776 773 'tempfile:': tempfilter,
777 774 'pipe:': pipefilter,
778 775 }
779 776
780 777 def filter(s, cmd):
781 778 "filter a string through a command that transforms its input to its output"
782 779 for name, fn in filtertable.iteritems():
783 780 if cmd.startswith(name):
784 781 return fn(s, cmd[len(name):].lstrip())
785 782 return pipefilter(s, cmd)
786 783
787 784 def binary(s):
788 785 """return true if a string is binary data"""
789 786 return bool(s and '\0' in s)
790 787
791 788 def increasingchunks(source, min=1024, max=65536):
792 789 '''return no less than min bytes per chunk while data remains,
793 790 doubling min after each chunk until it reaches max'''
794 791 def log2(x):
795 792 if not x:
796 793 return 0
797 794 i = 0
798 795 while x:
799 796 x >>= 1
800 797 i += 1
801 798 return i - 1
802 799
803 800 buf = []
804 801 blen = 0
805 802 for chunk in source:
806 803 buf.append(chunk)
807 804 blen += len(chunk)
808 805 if blen >= min:
809 806 if min < max:
810 807 min = min << 1
811 808 nmin = 1 << log2(blen)
812 809 if nmin > min:
813 810 min = nmin
814 811 if min > max:
815 812 min = max
816 813 yield ''.join(buf)
817 814 blen = 0
818 815 buf = []
819 816 if buf:
820 817 yield ''.join(buf)
821 818
822 819 Abort = error.Abort
823 820
824 821 def always(fn):
825 822 return True
826 823
827 824 def never(fn):
828 825 return False
829 826
830 827 def nogc(func):
831 828 """disable garbage collector
832 829
833 830 Python's garbage collector triggers a GC each time a certain number of
834 831 container objects (the number being defined by gc.get_threshold()) are
835 832 allocated even when marked not to be tracked by the collector. Tracking has
836 833 no effect on when GCs are triggered, only on what objects the GC looks
837 834 into. As a workaround, disable GC while building complex (huge)
838 835 containers.
839 836
840 837 This garbage collector issue have been fixed in 2.7.
841 838 """
842 839 def wrapper(*args, **kwargs):
843 840 gcenabled = gc.isenabled()
844 841 gc.disable()
845 842 try:
846 843 return func(*args, **kwargs)
847 844 finally:
848 845 if gcenabled:
849 846 gc.enable()
850 847 return wrapper
851 848
852 849 def pathto(root, n1, n2):
853 850 '''return the relative path from one place to another.
854 851 root should use os.sep to separate directories
855 852 n1 should use os.sep to separate directories
856 853 n2 should use "/" to separate directories
857 854 returns an os.sep-separated path.
858 855
859 856 If n1 is a relative path, it's assumed it's
860 857 relative to root.
861 858 n2 should always be relative to root.
862 859 '''
863 860 if not n1:
864 861 return localpath(n2)
865 862 if os.path.isabs(n1):
866 863 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
867 864 return os.path.join(root, localpath(n2))
868 865 n2 = '/'.join((pconvert(root), n2))
869 866 a, b = splitpath(n1), n2.split('/')
870 867 a.reverse()
871 868 b.reverse()
872 869 while a and b and a[-1] == b[-1]:
873 870 a.pop()
874 871 b.pop()
875 872 b.reverse()
876 873 return os.sep.join((['..'] * len(a)) + b) or '.'
877 874
878 875 def mainfrozen():
879 876 """return True if we are a frozen executable.
880 877
881 878 The code supports py2exe (most common, Windows only) and tools/freeze
882 879 (portable, not much used).
883 880 """
884 881 return (safehasattr(sys, "frozen") or # new py2exe
885 882 safehasattr(sys, "importers") or # old py2exe
886 883 imp.is_frozen("__main__")) # tools/freeze
887 884
888 885 # the location of data files matching the source code
889 886 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
890 887 # executable version (py2exe) doesn't support __file__
891 888 datapath = os.path.dirname(sys.executable)
892 889 else:
893 890 datapath = os.path.dirname(__file__)
894 891
895 892 i18n.setdatapath(datapath)
896 893
897 894 _hgexecutable = None
898 895
899 896 def hgexecutable():
900 897 """return location of the 'hg' executable.
901 898
902 899 Defaults to $HG or 'hg' in the search path.
903 900 """
904 901 if _hgexecutable is None:
905 902 hg = os.environ.get('HG')
906 903 mainmod = sys.modules['__main__']
907 904 if hg:
908 905 _sethgexecutable(hg)
909 906 elif mainfrozen():
910 907 if getattr(sys, 'frozen', None) == 'macosx_app':
911 908 # Env variable set by py2app
912 909 _sethgexecutable(os.environ['EXECUTABLEPATH'])
913 910 else:
914 911 _sethgexecutable(sys.executable)
915 912 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
916 913 _sethgexecutable(mainmod.__file__)
917 914 else:
918 915 exe = findexe('hg') or os.path.basename(sys.argv[0])
919 916 _sethgexecutable(exe)
920 917 return _hgexecutable
921 918
922 919 def _sethgexecutable(path):
923 920 """set location of the 'hg' executable"""
924 921 global _hgexecutable
925 922 _hgexecutable = path
926 923
927 924 def _isstdout(f):
928 925 fileno = getattr(f, 'fileno', None)
929 926 return fileno and fileno() == sys.__stdout__.fileno()
930 927
931 928 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
932 929 '''enhanced shell command execution.
933 930 run with environment maybe modified, maybe in different dir.
934 931
935 932 if command fails and onerr is None, return status, else raise onerr
936 933 object as exception.
937 934
938 935 if out is specified, it is assumed to be a file-like object that has a
939 936 write() method. stdout and stderr will be redirected to out.'''
940 937 if environ is None:
941 938 environ = {}
942 939 try:
943 940 sys.stdout.flush()
944 941 except Exception:
945 942 pass
946 943 def py2shell(val):
947 944 'convert python object into string that is useful to shell'
948 945 if val is None or val is False:
949 946 return '0'
950 947 if val is True:
951 948 return '1'
952 949 return str(val)
953 950 origcmd = cmd
954 951 cmd = quotecommand(cmd)
955 952 if sys.platform == 'plan9' and (sys.version_info[0] == 2
956 953 and sys.version_info[1] < 7):
957 954 # subprocess kludge to work around issues in half-baked Python
958 955 # ports, notably bichued/python:
959 956 if not cwd is None:
960 957 os.chdir(cwd)
961 958 rc = os.system(cmd)
962 959 else:
963 960 env = dict(os.environ)
964 961 env.update((k, py2shell(v)) for k, v in environ.iteritems())
965 962 env['HG'] = hgexecutable()
966 963 if out is None or _isstdout(out):
967 964 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
968 965 env=env, cwd=cwd)
969 966 else:
970 967 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
971 968 env=env, cwd=cwd, stdout=subprocess.PIPE,
972 969 stderr=subprocess.STDOUT)
973 970 while True:
974 971 line = proc.stdout.readline()
975 972 if not line:
976 973 break
977 974 out.write(line)
978 975 proc.wait()
979 976 rc = proc.returncode
980 977 if sys.platform == 'OpenVMS' and rc & 1:
981 978 rc = 0
982 979 if rc and onerr:
983 980 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
984 981 explainexit(rc)[0])
985 982 if errprefix:
986 983 errmsg = '%s: %s' % (errprefix, errmsg)
987 984 raise onerr(errmsg)
988 985 return rc
989 986
990 987 def checksignature(func):
991 988 '''wrap a function with code to check for calling errors'''
992 989 def check(*args, **kwargs):
993 990 try:
994 991 return func(*args, **kwargs)
995 992 except TypeError:
996 993 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
997 994 raise error.SignatureError
998 995 raise
999 996
1000 997 return check
1001 998
1002 999 def copyfile(src, dest, hardlink=False, copystat=False):
1003 1000 '''copy a file, preserving mode and optionally other stat info like
1004 1001 atime/mtime'''
1005 1002 if os.path.lexists(dest):
1006 1003 unlink(dest)
1007 1004 # hardlinks are problematic on CIFS, quietly ignore this flag
1008 1005 # until we find a way to work around it cleanly (issue4546)
1009 1006 if False and hardlink:
1010 1007 try:
1011 1008 oslink(src, dest)
1012 1009 return
1013 1010 except (IOError, OSError):
1014 1011 pass # fall back to normal copy
1015 1012 if os.path.islink(src):
1016 1013 os.symlink(os.readlink(src), dest)
1017 1014 # copytime is ignored for symlinks, but in general copytime isn't needed
1018 1015 # for them anyway
1019 1016 else:
1020 1017 try:
1021 1018 shutil.copyfile(src, dest)
1022 1019 if copystat:
1023 1020 # copystat also copies mode
1024 1021 shutil.copystat(src, dest)
1025 1022 else:
1026 1023 shutil.copymode(src, dest)
1027 1024 except shutil.Error as inst:
1028 1025 raise Abort(str(inst))
1029 1026
1030 1027 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1031 1028 """Copy a directory tree using hardlinks if possible."""
1032 1029 num = 0
1033 1030
1034 1031 if hardlink is None:
1035 1032 hardlink = (os.stat(src).st_dev ==
1036 1033 os.stat(os.path.dirname(dst)).st_dev)
1037 1034 if hardlink:
1038 1035 topic = _('linking')
1039 1036 else:
1040 1037 topic = _('copying')
1041 1038
1042 1039 if os.path.isdir(src):
1043 1040 os.mkdir(dst)
1044 1041 for name, kind in osutil.listdir(src):
1045 1042 srcname = os.path.join(src, name)
1046 1043 dstname = os.path.join(dst, name)
1047 1044 def nprog(t, pos):
1048 1045 if pos is not None:
1049 1046 return progress(t, pos + num)
1050 1047 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1051 1048 num += n
1052 1049 else:
1053 1050 if hardlink:
1054 1051 try:
1055 1052 oslink(src, dst)
1056 1053 except (IOError, OSError):
1057 1054 hardlink = False
1058 1055 shutil.copy(src, dst)
1059 1056 else:
1060 1057 shutil.copy(src, dst)
1061 1058 num += 1
1062 1059 progress(topic, num)
1063 1060 progress(topic, None)
1064 1061
1065 1062 return hardlink, num
1066 1063
1067 1064 _winreservednames = '''con prn aux nul
1068 1065 com1 com2 com3 com4 com5 com6 com7 com8 com9
1069 1066 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1070 1067 _winreservedchars = ':*?"<>|'
1071 1068 def checkwinfilename(path):
1072 1069 r'''Check that the base-relative path is a valid filename on Windows.
1073 1070 Returns None if the path is ok, or a UI string describing the problem.
1074 1071
1075 1072 >>> checkwinfilename("just/a/normal/path")
1076 1073 >>> checkwinfilename("foo/bar/con.xml")
1077 1074 "filename contains 'con', which is reserved on Windows"
1078 1075 >>> checkwinfilename("foo/con.xml/bar")
1079 1076 "filename contains 'con', which is reserved on Windows"
1080 1077 >>> checkwinfilename("foo/bar/xml.con")
1081 1078 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1082 1079 "filename contains 'AUX', which is reserved on Windows"
1083 1080 >>> checkwinfilename("foo/bar/bla:.txt")
1084 1081 "filename contains ':', which is reserved on Windows"
1085 1082 >>> checkwinfilename("foo/bar/b\07la.txt")
1086 1083 "filename contains '\\x07', which is invalid on Windows"
1087 1084 >>> checkwinfilename("foo/bar/bla ")
1088 1085 "filename ends with ' ', which is not allowed on Windows"
1089 1086 >>> checkwinfilename("../bar")
1090 1087 >>> checkwinfilename("foo\\")
1091 1088 "filename ends with '\\', which is invalid on Windows"
1092 1089 >>> checkwinfilename("foo\\/bar")
1093 1090 "directory name ends with '\\', which is invalid on Windows"
1094 1091 '''
1095 1092 if path.endswith('\\'):
1096 1093 return _("filename ends with '\\', which is invalid on Windows")
1097 1094 if '\\/' in path:
1098 1095 return _("directory name ends with '\\', which is invalid on Windows")
1099 1096 for n in path.replace('\\', '/').split('/'):
1100 1097 if not n:
1101 1098 continue
1102 1099 for c in n:
1103 1100 if c in _winreservedchars:
1104 1101 return _("filename contains '%s', which is reserved "
1105 1102 "on Windows") % c
1106 1103 if ord(c) <= 31:
1107 1104 return _("filename contains %r, which is invalid "
1108 1105 "on Windows") % c
1109 1106 base = n.split('.')[0]
1110 1107 if base and base.lower() in _winreservednames:
1111 1108 return _("filename contains '%s', which is reserved "
1112 1109 "on Windows") % base
1113 1110 t = n[-1]
1114 1111 if t in '. ' and n not in '..':
1115 1112 return _("filename ends with '%s', which is not allowed "
1116 1113 "on Windows") % t
1117 1114
1118 1115 if os.name == 'nt':
1119 1116 checkosfilename = checkwinfilename
1120 1117 else:
1121 1118 checkosfilename = platform.checkosfilename
1122 1119
1123 1120 def makelock(info, pathname):
1124 1121 try:
1125 1122 return os.symlink(info, pathname)
1126 1123 except OSError as why:
1127 1124 if why.errno == errno.EEXIST:
1128 1125 raise
1129 1126 except AttributeError: # no symlink in os
1130 1127 pass
1131 1128
1132 1129 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1133 1130 os.write(ld, info)
1134 1131 os.close(ld)
1135 1132
1136 1133 def readlock(pathname):
1137 1134 try:
1138 1135 return os.readlink(pathname)
1139 1136 except OSError as why:
1140 1137 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1141 1138 raise
1142 1139 except AttributeError: # no symlink in os
1143 1140 pass
1144 1141 fp = posixfile(pathname)
1145 1142 r = fp.read()
1146 1143 fp.close()
1147 1144 return r
1148 1145
1149 1146 def fstat(fp):
1150 1147 '''stat file object that may not have fileno method.'''
1151 1148 try:
1152 1149 return os.fstat(fp.fileno())
1153 1150 except AttributeError:
1154 1151 return os.stat(fp.name)
1155 1152
1156 1153 # File system features
1157 1154
1158 1155 def checkcase(path):
1159 1156 """
1160 1157 Return true if the given path is on a case-sensitive filesystem
1161 1158
1162 1159 Requires a path (like /foo/.hg) ending with a foldable final
1163 1160 directory component.
1164 1161 """
1165 1162 s1 = os.lstat(path)
1166 1163 d, b = os.path.split(path)
1167 1164 b2 = b.upper()
1168 1165 if b == b2:
1169 1166 b2 = b.lower()
1170 1167 if b == b2:
1171 1168 return True # no evidence against case sensitivity
1172 1169 p2 = os.path.join(d, b2)
1173 1170 try:
1174 1171 s2 = os.lstat(p2)
1175 1172 if s2 == s1:
1176 1173 return False
1177 1174 return True
1178 1175 except OSError:
1179 1176 return True
1180 1177
1181 1178 try:
1182 1179 import re2
1183 1180 _re2 = None
1184 1181 except ImportError:
1185 1182 _re2 = False
1186 1183
1187 1184 class _re(object):
1188 1185 def _checkre2(self):
1189 1186 global _re2
1190 1187 try:
1191 1188 # check if match works, see issue3964
1192 1189 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1193 1190 except ImportError:
1194 1191 _re2 = False
1195 1192
1196 1193 def compile(self, pat, flags=0):
1197 1194 '''Compile a regular expression, using re2 if possible
1198 1195
1199 1196 For best performance, use only re2-compatible regexp features. The
1200 1197 only flags from the re module that are re2-compatible are
1201 1198 IGNORECASE and MULTILINE.'''
1202 1199 if _re2 is None:
1203 1200 self._checkre2()
1204 1201 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1205 1202 if flags & remod.IGNORECASE:
1206 1203 pat = '(?i)' + pat
1207 1204 if flags & remod.MULTILINE:
1208 1205 pat = '(?m)' + pat
1209 1206 try:
1210 1207 return re2.compile(pat)
1211 1208 except re2.error:
1212 1209 pass
1213 1210 return remod.compile(pat, flags)
1214 1211
1215 1212 @propertycache
1216 1213 def escape(self):
1217 1214 '''Return the version of escape corresponding to self.compile.
1218 1215
1219 1216 This is imperfect because whether re2 or re is used for a particular
1220 1217 function depends on the flags, etc, but it's the best we can do.
1221 1218 '''
1222 1219 global _re2
1223 1220 if _re2 is None:
1224 1221 self._checkre2()
1225 1222 if _re2:
1226 1223 return re2.escape
1227 1224 else:
1228 1225 return remod.escape
1229 1226
1230 1227 re = _re()
1231 1228
1232 1229 _fspathcache = {}
1233 1230 def fspath(name, root):
1234 1231 '''Get name in the case stored in the filesystem
1235 1232
1236 1233 The name should be relative to root, and be normcase-ed for efficiency.
1237 1234
1238 1235 Note that this function is unnecessary, and should not be
1239 1236 called, for case-sensitive filesystems (simply because it's expensive).
1240 1237
1241 1238 The root should be normcase-ed, too.
1242 1239 '''
1243 1240 def _makefspathcacheentry(dir):
1244 1241 return dict((normcase(n), n) for n in os.listdir(dir))
1245 1242
1246 1243 seps = os.sep
1247 1244 if os.altsep:
1248 1245 seps = seps + os.altsep
1249 1246 # Protect backslashes. This gets silly very quickly.
1250 1247 seps.replace('\\','\\\\')
1251 1248 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1252 1249 dir = os.path.normpath(root)
1253 1250 result = []
1254 1251 for part, sep in pattern.findall(name):
1255 1252 if sep:
1256 1253 result.append(sep)
1257 1254 continue
1258 1255
1259 1256 if dir not in _fspathcache:
1260 1257 _fspathcache[dir] = _makefspathcacheentry(dir)
1261 1258 contents = _fspathcache[dir]
1262 1259
1263 1260 found = contents.get(part)
1264 1261 if not found:
1265 1262 # retry "once per directory" per "dirstate.walk" which
1266 1263 # may take place for each patches of "hg qpush", for example
1267 1264 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1268 1265 found = contents.get(part)
1269 1266
1270 1267 result.append(found or part)
1271 1268 dir = os.path.join(dir, part)
1272 1269
1273 1270 return ''.join(result)
1274 1271
1275 1272 def checknlink(testfile):
1276 1273 '''check whether hardlink count reporting works properly'''
1277 1274
1278 1275 # testfile may be open, so we need a separate file for checking to
1279 1276 # work around issue2543 (or testfile may get lost on Samba shares)
1280 1277 f1 = testfile + ".hgtmp1"
1281 1278 if os.path.lexists(f1):
1282 1279 return False
1283 1280 try:
1284 1281 posixfile(f1, 'w').close()
1285 1282 except IOError:
1286 1283 return False
1287 1284
1288 1285 f2 = testfile + ".hgtmp2"
1289 1286 fd = None
1290 1287 try:
1291 1288 oslink(f1, f2)
1292 1289 # nlinks() may behave differently for files on Windows shares if
1293 1290 # the file is open.
1294 1291 fd = posixfile(f2)
1295 1292 return nlinks(f2) > 1
1296 1293 except OSError:
1297 1294 return False
1298 1295 finally:
1299 1296 if fd is not None:
1300 1297 fd.close()
1301 1298 for f in (f1, f2):
1302 1299 try:
1303 1300 os.unlink(f)
1304 1301 except OSError:
1305 1302 pass
1306 1303
1307 1304 def endswithsep(path):
1308 1305 '''Check path ends with os.sep or os.altsep.'''
1309 1306 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1310 1307
1311 1308 def splitpath(path):
1312 1309 '''Split path by os.sep.
1313 1310 Note that this function does not use os.altsep because this is
1314 1311 an alternative of simple "xxx.split(os.sep)".
1315 1312 It is recommended to use os.path.normpath() before using this
1316 1313 function if need.'''
1317 1314 return path.split(os.sep)
1318 1315
1319 1316 def gui():
1320 1317 '''Are we running in a GUI?'''
1321 1318 if sys.platform == 'darwin':
1322 1319 if 'SSH_CONNECTION' in os.environ:
1323 1320 # handle SSH access to a box where the user is logged in
1324 1321 return False
1325 1322 elif getattr(osutil, 'isgui', None):
1326 1323 # check if a CoreGraphics session is available
1327 1324 return osutil.isgui()
1328 1325 else:
1329 1326 # pure build; use a safe default
1330 1327 return True
1331 1328 else:
1332 1329 return os.name == "nt" or os.environ.get("DISPLAY")
1333 1330
1334 1331 def mktempcopy(name, emptyok=False, createmode=None):
1335 1332 """Create a temporary file with the same contents from name
1336 1333
1337 1334 The permission bits are copied from the original file.
1338 1335
1339 1336 If the temporary file is going to be truncated immediately, you
1340 1337 can use emptyok=True as an optimization.
1341 1338
1342 1339 Returns the name of the temporary file.
1343 1340 """
1344 1341 d, fn = os.path.split(name)
1345 1342 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1346 1343 os.close(fd)
1347 1344 # Temporary files are created with mode 0600, which is usually not
1348 1345 # what we want. If the original file already exists, just copy
1349 1346 # its mode. Otherwise, manually obey umask.
1350 1347 copymode(name, temp, createmode)
1351 1348 if emptyok:
1352 1349 return temp
1353 1350 try:
1354 1351 try:
1355 1352 ifp = posixfile(name, "rb")
1356 1353 except IOError as inst:
1357 1354 if inst.errno == errno.ENOENT:
1358 1355 return temp
1359 1356 if not getattr(inst, 'filename', None):
1360 1357 inst.filename = name
1361 1358 raise
1362 1359 ofp = posixfile(temp, "wb")
1363 1360 for chunk in filechunkiter(ifp):
1364 1361 ofp.write(chunk)
1365 1362 ifp.close()
1366 1363 ofp.close()
1367 1364 except: # re-raises
1368 1365 try: os.unlink(temp)
1369 1366 except OSError: pass
1370 1367 raise
1371 1368 return temp
1372 1369
1373 1370 class atomictempfile(object):
1374 1371 '''writable file object that atomically updates a file
1375 1372
1376 1373 All writes will go to a temporary copy of the original file. Call
1377 1374 close() when you are done writing, and atomictempfile will rename
1378 1375 the temporary copy to the original name, making the changes
1379 1376 visible. If the object is destroyed without being closed, all your
1380 1377 writes are discarded.
1381 1378 '''
1382 1379 def __init__(self, name, mode='w+b', createmode=None):
1383 1380 self.__name = name # permanent name
1384 1381 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1385 1382 createmode=createmode)
1386 1383 self._fp = posixfile(self._tempname, mode)
1387 1384
1388 1385 # delegated methods
1389 1386 self.write = self._fp.write
1390 1387 self.seek = self._fp.seek
1391 1388 self.tell = self._fp.tell
1392 1389 self.fileno = self._fp.fileno
1393 1390
1394 1391 def close(self):
1395 1392 if not self._fp.closed:
1396 1393 self._fp.close()
1397 1394 rename(self._tempname, localpath(self.__name))
1398 1395
1399 1396 def discard(self):
1400 1397 if not self._fp.closed:
1401 1398 try:
1402 1399 os.unlink(self._tempname)
1403 1400 except OSError:
1404 1401 pass
1405 1402 self._fp.close()
1406 1403
1407 1404 def __del__(self):
1408 1405 if safehasattr(self, '_fp'): # constructor actually did something
1409 1406 self.discard()
1410 1407
1411 1408 def makedirs(name, mode=None, notindexed=False):
1412 1409 """recursive directory creation with parent mode inheritance"""
1413 1410 try:
1414 1411 makedir(name, notindexed)
1415 1412 except OSError as err:
1416 1413 if err.errno == errno.EEXIST:
1417 1414 return
1418 1415 if err.errno != errno.ENOENT or not name:
1419 1416 raise
1420 1417 parent = os.path.dirname(os.path.abspath(name))
1421 1418 if parent == name:
1422 1419 raise
1423 1420 makedirs(parent, mode, notindexed)
1424 1421 makedir(name, notindexed)
1425 1422 if mode is not None:
1426 1423 os.chmod(name, mode)
1427 1424
1428 1425 def ensuredirs(name, mode=None, notindexed=False):
1429 1426 """race-safe recursive directory creation
1430 1427
1431 1428 Newly created directories are marked as "not to be indexed by
1432 1429 the content indexing service", if ``notindexed`` is specified
1433 1430 for "write" mode access.
1434 1431 """
1435 1432 if os.path.isdir(name):
1436 1433 return
1437 1434 parent = os.path.dirname(os.path.abspath(name))
1438 1435 if parent != name:
1439 1436 ensuredirs(parent, mode, notindexed)
1440 1437 try:
1441 1438 makedir(name, notindexed)
1442 1439 except OSError as err:
1443 1440 if err.errno == errno.EEXIST and os.path.isdir(name):
1444 1441 # someone else seems to have won a directory creation race
1445 1442 return
1446 1443 raise
1447 1444 if mode is not None:
1448 1445 os.chmod(name, mode)
1449 1446
1450 1447 def readfile(path):
1451 1448 fp = open(path, 'rb')
1452 1449 try:
1453 1450 return fp.read()
1454 1451 finally:
1455 1452 fp.close()
1456 1453
1457 1454 def writefile(path, text):
1458 1455 fp = open(path, 'wb')
1459 1456 try:
1460 1457 fp.write(text)
1461 1458 finally:
1462 1459 fp.close()
1463 1460
1464 1461 def appendfile(path, text):
1465 1462 fp = open(path, 'ab')
1466 1463 try:
1467 1464 fp.write(text)
1468 1465 finally:
1469 1466 fp.close()
1470 1467
1471 1468 class chunkbuffer(object):
1472 1469 """Allow arbitrary sized chunks of data to be efficiently read from an
1473 1470 iterator over chunks of arbitrary size."""
1474 1471
1475 1472 def __init__(self, in_iter):
1476 1473 """in_iter is the iterator that's iterating over the input chunks.
1477 1474 targetsize is how big a buffer to try to maintain."""
1478 1475 def splitbig(chunks):
1479 1476 for chunk in chunks:
1480 1477 if len(chunk) > 2**20:
1481 1478 pos = 0
1482 1479 while pos < len(chunk):
1483 1480 end = pos + 2 ** 18
1484 1481 yield chunk[pos:end]
1485 1482 pos = end
1486 1483 else:
1487 1484 yield chunk
1488 1485 self.iter = splitbig(in_iter)
1489 1486 self._queue = collections.deque()
1490 1487 self._chunkoffset = 0
1491 1488
1492 1489 def read(self, l=None):
1493 1490 """Read L bytes of data from the iterator of chunks of data.
1494 1491 Returns less than L bytes if the iterator runs dry.
1495 1492
1496 1493 If size parameter is omitted, read everything"""
1497 1494 if l is None:
1498 1495 return ''.join(self.iter)
1499 1496
1500 1497 left = l
1501 1498 buf = []
1502 1499 queue = self._queue
1503 1500 while left > 0:
1504 1501 # refill the queue
1505 1502 if not queue:
1506 1503 target = 2**18
1507 1504 for chunk in self.iter:
1508 1505 queue.append(chunk)
1509 1506 target -= len(chunk)
1510 1507 if target <= 0:
1511 1508 break
1512 1509 if not queue:
1513 1510 break
1514 1511
1515 1512 # The easy way to do this would be to queue.popleft(), modify the
1516 1513 # chunk (if necessary), then queue.appendleft(). However, for cases
1517 1514 # where we read partial chunk content, this incurs 2 dequeue
1518 1515 # mutations and creates a new str for the remaining chunk in the
1519 1516 # queue. Our code below avoids this overhead.
1520 1517
1521 1518 chunk = queue[0]
1522 1519 chunkl = len(chunk)
1523 1520 offset = self._chunkoffset
1524 1521
1525 1522 # Use full chunk.
1526 1523 if offset == 0 and left >= chunkl:
1527 1524 left -= chunkl
1528 1525 queue.popleft()
1529 1526 buf.append(chunk)
1530 1527 # self._chunkoffset remains at 0.
1531 1528 continue
1532 1529
1533 1530 chunkremaining = chunkl - offset
1534 1531
1535 1532 # Use all of unconsumed part of chunk.
1536 1533 if left >= chunkremaining:
1537 1534 left -= chunkremaining
1538 1535 queue.popleft()
1539 1536 # offset == 0 is enabled by block above, so this won't merely
1540 1537 # copy via ``chunk[0:]``.
1541 1538 buf.append(chunk[offset:])
1542 1539 self._chunkoffset = 0
1543 1540
1544 1541 # Partial chunk needed.
1545 1542 else:
1546 1543 buf.append(chunk[offset:offset + left])
1547 1544 self._chunkoffset += left
1548 1545 left -= chunkremaining
1549 1546
1550 1547 return ''.join(buf)
1551 1548
1552 1549 def filechunkiter(f, size=65536, limit=None):
1553 1550 """Create a generator that produces the data in the file size
1554 1551 (default 65536) bytes at a time, up to optional limit (default is
1555 1552 to read all data). Chunks may be less than size bytes if the
1556 1553 chunk is the last chunk in the file, or the file is a socket or
1557 1554 some other type of file that sometimes reads less data than is
1558 1555 requested."""
1559 1556 assert size >= 0
1560 1557 assert limit is None or limit >= 0
1561 1558 while True:
1562 1559 if limit is None:
1563 1560 nbytes = size
1564 1561 else:
1565 1562 nbytes = min(limit, size)
1566 1563 s = nbytes and f.read(nbytes)
1567 1564 if not s:
1568 1565 break
1569 1566 if limit:
1570 1567 limit -= len(s)
1571 1568 yield s
1572 1569
1573 1570 def makedate(timestamp=None):
1574 1571 '''Return a unix timestamp (or the current time) as a (unixtime,
1575 1572 offset) tuple based off the local timezone.'''
1576 1573 if timestamp is None:
1577 1574 timestamp = time.time()
1578 1575 if timestamp < 0:
1579 1576 hint = _("check your clock")
1580 1577 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1581 1578 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1582 1579 datetime.datetime.fromtimestamp(timestamp))
1583 1580 tz = delta.days * 86400 + delta.seconds
1584 1581 return timestamp, tz
1585 1582
1586 1583 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1587 1584 """represent a (unixtime, offset) tuple as a localized time.
1588 1585 unixtime is seconds since the epoch, and offset is the time zone's
1589 1586 number of seconds away from UTC. if timezone is false, do not
1590 1587 append time zone to string."""
1591 1588 t, tz = date or makedate()
1592 1589 if t < 0:
1593 1590 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1594 1591 tz = 0
1595 1592 if "%1" in format or "%2" in format or "%z" in format:
1596 1593 sign = (tz > 0) and "-" or "+"
1597 1594 minutes = abs(tz) // 60
1598 1595 q, r = divmod(minutes, 60)
1599 1596 format = format.replace("%z", "%1%2")
1600 1597 format = format.replace("%1", "%c%02d" % (sign, q))
1601 1598 format = format.replace("%2", "%02d" % r)
1602 1599 try:
1603 1600 t = time.gmtime(float(t) - tz)
1604 1601 except ValueError:
1605 1602 # time was out of range
1606 1603 t = time.gmtime(sys.maxint)
1607 1604 s = time.strftime(format, t)
1608 1605 return s
1609 1606
1610 1607 def shortdate(date=None):
1611 1608 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1612 1609 return datestr(date, format='%Y-%m-%d')
1613 1610
1614 1611 def parsetimezone(tz):
1615 1612 """parse a timezone string and return an offset integer"""
1616 1613 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1617 1614 sign = (tz[0] == "+") and 1 or -1
1618 1615 hours = int(tz[1:3])
1619 1616 minutes = int(tz[3:5])
1620 1617 return -sign * (hours * 60 + minutes) * 60
1621 1618 if tz == "GMT" or tz == "UTC":
1622 1619 return 0
1623 1620 return None
1624 1621
1625 1622 def strdate(string, format, defaults=[]):
1626 1623 """parse a localized time string and return a (unixtime, offset) tuple.
1627 1624 if the string cannot be parsed, ValueError is raised."""
1628 1625 # NOTE: unixtime = localunixtime + offset
1629 1626 offset, date = parsetimezone(string.split()[-1]), string
1630 1627 if offset is not None:
1631 1628 date = " ".join(string.split()[:-1])
1632 1629
1633 1630 # add missing elements from defaults
1634 1631 usenow = False # default to using biased defaults
1635 1632 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1636 1633 found = [True for p in part if ("%"+p) in format]
1637 1634 if not found:
1638 1635 date += "@" + defaults[part][usenow]
1639 1636 format += "@%" + part[0]
1640 1637 else:
1641 1638 # We've found a specific time element, less specific time
1642 1639 # elements are relative to today
1643 1640 usenow = True
1644 1641
1645 1642 timetuple = time.strptime(date, format)
1646 1643 localunixtime = int(calendar.timegm(timetuple))
1647 1644 if offset is None:
1648 1645 # local timezone
1649 1646 unixtime = int(time.mktime(timetuple))
1650 1647 offset = unixtime - localunixtime
1651 1648 else:
1652 1649 unixtime = localunixtime + offset
1653 1650 return unixtime, offset
1654 1651
1655 1652 def parsedate(date, formats=None, bias=None):
1656 1653 """parse a localized date/time and return a (unixtime, offset) tuple.
1657 1654
1658 1655 The date may be a "unixtime offset" string or in one of the specified
1659 1656 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1660 1657
1661 1658 >>> parsedate(' today ') == parsedate(\
1662 1659 datetime.date.today().strftime('%b %d'))
1663 1660 True
1664 1661 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1665 1662 datetime.timedelta(days=1)\
1666 1663 ).strftime('%b %d'))
1667 1664 True
1668 1665 >>> now, tz = makedate()
1669 1666 >>> strnow, strtz = parsedate('now')
1670 1667 >>> (strnow - now) < 1
1671 1668 True
1672 1669 >>> tz == strtz
1673 1670 True
1674 1671 """
1675 1672 if bias is None:
1676 1673 bias = {}
1677 1674 if not date:
1678 1675 return 0, 0
1679 1676 if isinstance(date, tuple) and len(date) == 2:
1680 1677 return date
1681 1678 if not formats:
1682 1679 formats = defaultdateformats
1683 1680 date = date.strip()
1684 1681
1685 1682 if date == 'now' or date == _('now'):
1686 1683 return makedate()
1687 1684 if date == 'today' or date == _('today'):
1688 1685 date = datetime.date.today().strftime('%b %d')
1689 1686 elif date == 'yesterday' or date == _('yesterday'):
1690 1687 date = (datetime.date.today() -
1691 1688 datetime.timedelta(days=1)).strftime('%b %d')
1692 1689
1693 1690 try:
1694 1691 when, offset = map(int, date.split(' '))
1695 1692 except ValueError:
1696 1693 # fill out defaults
1697 1694 now = makedate()
1698 1695 defaults = {}
1699 1696 for part in ("d", "mb", "yY", "HI", "M", "S"):
1700 1697 # this piece is for rounding the specific end of unknowns
1701 1698 b = bias.get(part)
1702 1699 if b is None:
1703 1700 if part[0] in "HMS":
1704 1701 b = "00"
1705 1702 else:
1706 1703 b = "0"
1707 1704
1708 1705 # this piece is for matching the generic end to today's date
1709 1706 n = datestr(now, "%" + part[0])
1710 1707
1711 1708 defaults[part] = (b, n)
1712 1709
1713 1710 for format in formats:
1714 1711 try:
1715 1712 when, offset = strdate(date, format, defaults)
1716 1713 except (ValueError, OverflowError):
1717 1714 pass
1718 1715 else:
1719 1716 break
1720 1717 else:
1721 1718 raise Abort(_('invalid date: %r') % date)
1722 1719 # validate explicit (probably user-specified) date and
1723 1720 # time zone offset. values must fit in signed 32 bits for
1724 1721 # current 32-bit linux runtimes. timezones go from UTC-12
1725 1722 # to UTC+14
1726 1723 if abs(when) > 0x7fffffff:
1727 1724 raise Abort(_('date exceeds 32 bits: %d') % when)
1728 1725 if when < 0:
1729 1726 raise Abort(_('negative date value: %d') % when)
1730 1727 if offset < -50400 or offset > 43200:
1731 1728 raise Abort(_('impossible time zone offset: %d') % offset)
1732 1729 return when, offset
1733 1730
1734 1731 def matchdate(date):
1735 1732 """Return a function that matches a given date match specifier
1736 1733
1737 1734 Formats include:
1738 1735
1739 1736 '{date}' match a given date to the accuracy provided
1740 1737
1741 1738 '<{date}' on or before a given date
1742 1739
1743 1740 '>{date}' on or after a given date
1744 1741
1745 1742 >>> p1 = parsedate("10:29:59")
1746 1743 >>> p2 = parsedate("10:30:00")
1747 1744 >>> p3 = parsedate("10:30:59")
1748 1745 >>> p4 = parsedate("10:31:00")
1749 1746 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1750 1747 >>> f = matchdate("10:30")
1751 1748 >>> f(p1[0])
1752 1749 False
1753 1750 >>> f(p2[0])
1754 1751 True
1755 1752 >>> f(p3[0])
1756 1753 True
1757 1754 >>> f(p4[0])
1758 1755 False
1759 1756 >>> f(p5[0])
1760 1757 False
1761 1758 """
1762 1759
1763 1760 def lower(date):
1764 1761 d = {'mb': "1", 'd': "1"}
1765 1762 return parsedate(date, extendeddateformats, d)[0]
1766 1763
1767 1764 def upper(date):
1768 1765 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1769 1766 for days in ("31", "30", "29"):
1770 1767 try:
1771 1768 d["d"] = days
1772 1769 return parsedate(date, extendeddateformats, d)[0]
1773 1770 except Abort:
1774 1771 pass
1775 1772 d["d"] = "28"
1776 1773 return parsedate(date, extendeddateformats, d)[0]
1777 1774
1778 1775 date = date.strip()
1779 1776
1780 1777 if not date:
1781 1778 raise Abort(_("dates cannot consist entirely of whitespace"))
1782 1779 elif date[0] == "<":
1783 1780 if not date[1:]:
1784 1781 raise Abort(_("invalid day spec, use '<DATE'"))
1785 1782 when = upper(date[1:])
1786 1783 return lambda x: x <= when
1787 1784 elif date[0] == ">":
1788 1785 if not date[1:]:
1789 1786 raise Abort(_("invalid day spec, use '>DATE'"))
1790 1787 when = lower(date[1:])
1791 1788 return lambda x: x >= when
1792 1789 elif date[0] == "-":
1793 1790 try:
1794 1791 days = int(date[1:])
1795 1792 except ValueError:
1796 1793 raise Abort(_("invalid day spec: %s") % date[1:])
1797 1794 if days < 0:
1798 1795 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1799 1796 % date[1:])
1800 1797 when = makedate()[0] - days * 3600 * 24
1801 1798 return lambda x: x >= when
1802 1799 elif " to " in date:
1803 1800 a, b = date.split(" to ")
1804 1801 start, stop = lower(a), upper(b)
1805 1802 return lambda x: x >= start and x <= stop
1806 1803 else:
1807 1804 start, stop = lower(date), upper(date)
1808 1805 return lambda x: x >= start and x <= stop
1809 1806
1810 1807 def stringmatcher(pattern):
1811 1808 """
1812 1809 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1813 1810 returns the matcher name, pattern, and matcher function.
1814 1811 missing or unknown prefixes are treated as literal matches.
1815 1812
1816 1813 helper for tests:
1817 1814 >>> def test(pattern, *tests):
1818 1815 ... kind, pattern, matcher = stringmatcher(pattern)
1819 1816 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1820 1817
1821 1818 exact matching (no prefix):
1822 1819 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1823 1820 ('literal', 'abcdefg', [False, False, True])
1824 1821
1825 1822 regex matching ('re:' prefix)
1826 1823 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1827 1824 ('re', 'a.+b', [False, False, True])
1828 1825
1829 1826 force exact matches ('literal:' prefix)
1830 1827 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1831 1828 ('literal', 're:foobar', [False, True])
1832 1829
1833 1830 unknown prefixes are ignored and treated as literals
1834 1831 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1835 1832 ('literal', 'foo:bar', [False, False, True])
1836 1833 """
1837 1834 if pattern.startswith('re:'):
1838 1835 pattern = pattern[3:]
1839 1836 try:
1840 1837 regex = remod.compile(pattern)
1841 1838 except remod.error as e:
1842 1839 raise error.ParseError(_('invalid regular expression: %s')
1843 1840 % e)
1844 1841 return 're', pattern, regex.search
1845 1842 elif pattern.startswith('literal:'):
1846 1843 pattern = pattern[8:]
1847 1844 return 'literal', pattern, pattern.__eq__
1848 1845
1849 1846 def shortuser(user):
1850 1847 """Return a short representation of a user name or email address."""
1851 1848 f = user.find('@')
1852 1849 if f >= 0:
1853 1850 user = user[:f]
1854 1851 f = user.find('<')
1855 1852 if f >= 0:
1856 1853 user = user[f + 1:]
1857 1854 f = user.find(' ')
1858 1855 if f >= 0:
1859 1856 user = user[:f]
1860 1857 f = user.find('.')
1861 1858 if f >= 0:
1862 1859 user = user[:f]
1863 1860 return user
1864 1861
1865 1862 def emailuser(user):
1866 1863 """Return the user portion of an email address."""
1867 1864 f = user.find('@')
1868 1865 if f >= 0:
1869 1866 user = user[:f]
1870 1867 f = user.find('<')
1871 1868 if f >= 0:
1872 1869 user = user[f + 1:]
1873 1870 return user
1874 1871
1875 1872 def email(author):
1876 1873 '''get email of author.'''
1877 1874 r = author.find('>')
1878 1875 if r == -1:
1879 1876 r = None
1880 1877 return author[author.find('<') + 1:r]
1881 1878
1882 1879 def ellipsis(text, maxlength=400):
1883 1880 """Trim string to at most maxlength (default: 400) columns in display."""
1884 1881 return encoding.trim(text, maxlength, ellipsis='...')
1885 1882
1886 1883 def unitcountfn(*unittable):
1887 1884 '''return a function that renders a readable count of some quantity'''
1888 1885
1889 1886 def go(count):
1890 1887 for multiplier, divisor, format in unittable:
1891 1888 if count >= divisor * multiplier:
1892 1889 return format % (count / float(divisor))
1893 1890 return unittable[-1][2] % count
1894 1891
1895 1892 return go
1896 1893
1897 1894 bytecount = unitcountfn(
1898 1895 (100, 1 << 30, _('%.0f GB')),
1899 1896 (10, 1 << 30, _('%.1f GB')),
1900 1897 (1, 1 << 30, _('%.2f GB')),
1901 1898 (100, 1 << 20, _('%.0f MB')),
1902 1899 (10, 1 << 20, _('%.1f MB')),
1903 1900 (1, 1 << 20, _('%.2f MB')),
1904 1901 (100, 1 << 10, _('%.0f KB')),
1905 1902 (10, 1 << 10, _('%.1f KB')),
1906 1903 (1, 1 << 10, _('%.2f KB')),
1907 1904 (1, 1, _('%.0f bytes')),
1908 1905 )
1909 1906
1910 1907 def uirepr(s):
1911 1908 # Avoid double backslash in Windows path repr()
1912 1909 return repr(s).replace('\\\\', '\\')
1913 1910
1914 1911 # delay import of textwrap
1915 1912 def MBTextWrapper(**kwargs):
1916 1913 class tw(textwrap.TextWrapper):
1917 1914 """
1918 1915 Extend TextWrapper for width-awareness.
1919 1916
1920 1917 Neither number of 'bytes' in any encoding nor 'characters' is
1921 1918 appropriate to calculate terminal columns for specified string.
1922 1919
1923 1920 Original TextWrapper implementation uses built-in 'len()' directly,
1924 1921 so overriding is needed to use width information of each characters.
1925 1922
1926 1923 In addition, characters classified into 'ambiguous' width are
1927 1924 treated as wide in East Asian area, but as narrow in other.
1928 1925
1929 1926 This requires use decision to determine width of such characters.
1930 1927 """
1931 1928 def _cutdown(self, ucstr, space_left):
1932 1929 l = 0
1933 1930 colwidth = encoding.ucolwidth
1934 1931 for i in xrange(len(ucstr)):
1935 1932 l += colwidth(ucstr[i])
1936 1933 if space_left < l:
1937 1934 return (ucstr[:i], ucstr[i:])
1938 1935 return ucstr, ''
1939 1936
1940 1937 # overriding of base class
1941 1938 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1942 1939 space_left = max(width - cur_len, 1)
1943 1940
1944 1941 if self.break_long_words:
1945 1942 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1946 1943 cur_line.append(cut)
1947 1944 reversed_chunks[-1] = res
1948 1945 elif not cur_line:
1949 1946 cur_line.append(reversed_chunks.pop())
1950 1947
1951 1948 # this overriding code is imported from TextWrapper of Python 2.6
1952 1949 # to calculate columns of string by 'encoding.ucolwidth()'
1953 1950 def _wrap_chunks(self, chunks):
1954 1951 colwidth = encoding.ucolwidth
1955 1952
1956 1953 lines = []
1957 1954 if self.width <= 0:
1958 1955 raise ValueError("invalid width %r (must be > 0)" % self.width)
1959 1956
1960 1957 # Arrange in reverse order so items can be efficiently popped
1961 1958 # from a stack of chucks.
1962 1959 chunks.reverse()
1963 1960
1964 1961 while chunks:
1965 1962
1966 1963 # Start the list of chunks that will make up the current line.
1967 1964 # cur_len is just the length of all the chunks in cur_line.
1968 1965 cur_line = []
1969 1966 cur_len = 0
1970 1967
1971 1968 # Figure out which static string will prefix this line.
1972 1969 if lines:
1973 1970 indent = self.subsequent_indent
1974 1971 else:
1975 1972 indent = self.initial_indent
1976 1973
1977 1974 # Maximum width for this line.
1978 1975 width = self.width - len(indent)
1979 1976
1980 1977 # First chunk on line is whitespace -- drop it, unless this
1981 1978 # is the very beginning of the text (i.e. no lines started yet).
1982 1979 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1983 1980 del chunks[-1]
1984 1981
1985 1982 while chunks:
1986 1983 l = colwidth(chunks[-1])
1987 1984
1988 1985 # Can at least squeeze this chunk onto the current line.
1989 1986 if cur_len + l <= width:
1990 1987 cur_line.append(chunks.pop())
1991 1988 cur_len += l
1992 1989
1993 1990 # Nope, this line is full.
1994 1991 else:
1995 1992 break
1996 1993
1997 1994 # The current line is full, and the next chunk is too big to
1998 1995 # fit on *any* line (not just this one).
1999 1996 if chunks and colwidth(chunks[-1]) > width:
2000 1997 self._handle_long_word(chunks, cur_line, cur_len, width)
2001 1998
2002 1999 # If the last chunk on this line is all whitespace, drop it.
2003 2000 if (self.drop_whitespace and
2004 2001 cur_line and cur_line[-1].strip() == ''):
2005 2002 del cur_line[-1]
2006 2003
2007 2004 # Convert current line back to a string and store it in list
2008 2005 # of all lines (return value).
2009 2006 if cur_line:
2010 2007 lines.append(indent + ''.join(cur_line))
2011 2008
2012 2009 return lines
2013 2010
2014 2011 global MBTextWrapper
2015 2012 MBTextWrapper = tw
2016 2013 return tw(**kwargs)
2017 2014
2018 2015 def wrap(line, width, initindent='', hangindent=''):
2019 2016 maxindent = max(len(hangindent), len(initindent))
2020 2017 if width <= maxindent:
2021 2018 # adjust for weird terminal size
2022 2019 width = max(78, maxindent + 1)
2023 2020 line = line.decode(encoding.encoding, encoding.encodingmode)
2024 2021 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2025 2022 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2026 2023 wrapper = MBTextWrapper(width=width,
2027 2024 initial_indent=initindent,
2028 2025 subsequent_indent=hangindent)
2029 2026 return wrapper.fill(line).encode(encoding.encoding)
2030 2027
2031 2028 def iterlines(iterator):
2032 2029 for chunk in iterator:
2033 2030 for line in chunk.splitlines():
2034 2031 yield line
2035 2032
2036 2033 def expandpath(path):
2037 2034 return os.path.expanduser(os.path.expandvars(path))
2038 2035
2039 2036 def hgcmd():
2040 2037 """Return the command used to execute current hg
2041 2038
2042 2039 This is different from hgexecutable() because on Windows we want
2043 2040 to avoid things opening new shell windows like batch files, so we
2044 2041 get either the python call or current executable.
2045 2042 """
2046 2043 if mainfrozen():
2047 2044 if getattr(sys, 'frozen', None) == 'macosx_app':
2048 2045 # Env variable set by py2app
2049 2046 return [os.environ['EXECUTABLEPATH']]
2050 2047 else:
2051 2048 return [sys.executable]
2052 2049 return gethgcmd()
2053 2050
2054 2051 def rundetached(args, condfn):
2055 2052 """Execute the argument list in a detached process.
2056 2053
2057 2054 condfn is a callable which is called repeatedly and should return
2058 2055 True once the child process is known to have started successfully.
2059 2056 At this point, the child process PID is returned. If the child
2060 2057 process fails to start or finishes before condfn() evaluates to
2061 2058 True, return -1.
2062 2059 """
2063 2060 # Windows case is easier because the child process is either
2064 2061 # successfully starting and validating the condition or exiting
2065 2062 # on failure. We just poll on its PID. On Unix, if the child
2066 2063 # process fails to start, it will be left in a zombie state until
2067 2064 # the parent wait on it, which we cannot do since we expect a long
2068 2065 # running process on success. Instead we listen for SIGCHLD telling
2069 2066 # us our child process terminated.
2070 2067 terminated = set()
2071 2068 def handler(signum, frame):
2072 2069 terminated.add(os.wait())
2073 2070 prevhandler = None
2074 2071 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2075 2072 if SIGCHLD is not None:
2076 2073 prevhandler = signal.signal(SIGCHLD, handler)
2077 2074 try:
2078 2075 pid = spawndetached(args)
2079 2076 while not condfn():
2080 2077 if ((pid in terminated or not testpid(pid))
2081 2078 and not condfn()):
2082 2079 return -1
2083 2080 time.sleep(0.1)
2084 2081 return pid
2085 2082 finally:
2086 2083 if prevhandler is not None:
2087 2084 signal.signal(signal.SIGCHLD, prevhandler)
2088 2085
2089 2086 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2090 2087 """Return the result of interpolating items in the mapping into string s.
2091 2088
2092 2089 prefix is a single character string, or a two character string with
2093 2090 a backslash as the first character if the prefix needs to be escaped in
2094 2091 a regular expression.
2095 2092
2096 2093 fn is an optional function that will be applied to the replacement text
2097 2094 just before replacement.
2098 2095
2099 2096 escape_prefix is an optional flag that allows using doubled prefix for
2100 2097 its escaping.
2101 2098 """
2102 2099 fn = fn or (lambda s: s)
2103 2100 patterns = '|'.join(mapping.keys())
2104 2101 if escape_prefix:
2105 2102 patterns += '|' + prefix
2106 2103 if len(prefix) > 1:
2107 2104 prefix_char = prefix[1:]
2108 2105 else:
2109 2106 prefix_char = prefix
2110 2107 mapping[prefix_char] = prefix_char
2111 2108 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2112 2109 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2113 2110
2114 2111 def getport(port):
2115 2112 """Return the port for a given network service.
2116 2113
2117 2114 If port is an integer, it's returned as is. If it's a string, it's
2118 2115 looked up using socket.getservbyname(). If there's no matching
2119 2116 service, error.Abort is raised.
2120 2117 """
2121 2118 try:
2122 2119 return int(port)
2123 2120 except ValueError:
2124 2121 pass
2125 2122
2126 2123 try:
2127 2124 return socket.getservbyname(port)
2128 2125 except socket.error:
2129 2126 raise Abort(_("no port number associated with service '%s'") % port)
2130 2127
2131 2128 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2132 2129 '0': False, 'no': False, 'false': False, 'off': False,
2133 2130 'never': False}
2134 2131
2135 2132 def parsebool(s):
2136 2133 """Parse s into a boolean.
2137 2134
2138 2135 If s is not a valid boolean, returns None.
2139 2136 """
2140 2137 return _booleans.get(s.lower(), None)
2141 2138
2142 2139 _hexdig = '0123456789ABCDEFabcdef'
2143 2140 _hextochr = dict((a + b, chr(int(a + b, 16)))
2144 2141 for a in _hexdig for b in _hexdig)
2145 2142
2146 2143 def _urlunquote(s):
2147 2144 """Decode HTTP/HTML % encoding.
2148 2145
2149 2146 >>> _urlunquote('abc%20def')
2150 2147 'abc def'
2151 2148 """
2152 2149 res = s.split('%')
2153 2150 # fastpath
2154 2151 if len(res) == 1:
2155 2152 return s
2156 2153 s = res[0]
2157 2154 for item in res[1:]:
2158 2155 try:
2159 2156 s += _hextochr[item[:2]] + item[2:]
2160 2157 except KeyError:
2161 2158 s += '%' + item
2162 2159 except UnicodeDecodeError:
2163 2160 s += unichr(int(item[:2], 16)) + item[2:]
2164 2161 return s
2165 2162
2166 2163 class url(object):
2167 2164 r"""Reliable URL parser.
2168 2165
2169 2166 This parses URLs and provides attributes for the following
2170 2167 components:
2171 2168
2172 2169 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2173 2170
2174 2171 Missing components are set to None. The only exception is
2175 2172 fragment, which is set to '' if present but empty.
2176 2173
2177 2174 If parsefragment is False, fragment is included in query. If
2178 2175 parsequery is False, query is included in path. If both are
2179 2176 False, both fragment and query are included in path.
2180 2177
2181 2178 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2182 2179
2183 2180 Note that for backward compatibility reasons, bundle URLs do not
2184 2181 take host names. That means 'bundle://../' has a path of '../'.
2185 2182
2186 2183 Examples:
2187 2184
2188 2185 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2189 2186 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2190 2187 >>> url('ssh://[::1]:2200//home/joe/repo')
2191 2188 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2192 2189 >>> url('file:///home/joe/repo')
2193 2190 <url scheme: 'file', path: '/home/joe/repo'>
2194 2191 >>> url('file:///c:/temp/foo/')
2195 2192 <url scheme: 'file', path: 'c:/temp/foo/'>
2196 2193 >>> url('bundle:foo')
2197 2194 <url scheme: 'bundle', path: 'foo'>
2198 2195 >>> url('bundle://../foo')
2199 2196 <url scheme: 'bundle', path: '../foo'>
2200 2197 >>> url(r'c:\foo\bar')
2201 2198 <url path: 'c:\\foo\\bar'>
2202 2199 >>> url(r'\\blah\blah\blah')
2203 2200 <url path: '\\\\blah\\blah\\blah'>
2204 2201 >>> url(r'\\blah\blah\blah#baz')
2205 2202 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2206 2203 >>> url(r'file:///C:\users\me')
2207 2204 <url scheme: 'file', path: 'C:\\users\\me'>
2208 2205
2209 2206 Authentication credentials:
2210 2207
2211 2208 >>> url('ssh://joe:xyz@x/repo')
2212 2209 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2213 2210 >>> url('ssh://joe@x/repo')
2214 2211 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2215 2212
2216 2213 Query strings and fragments:
2217 2214
2218 2215 >>> url('http://host/a?b#c')
2219 2216 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2220 2217 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2221 2218 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2222 2219 """
2223 2220
2224 2221 _safechars = "!~*'()+"
2225 2222 _safepchars = "/!~*'()+:\\"
2226 2223 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2227 2224
2228 2225 def __init__(self, path, parsequery=True, parsefragment=True):
2229 2226 # We slowly chomp away at path until we have only the path left
2230 2227 self.scheme = self.user = self.passwd = self.host = None
2231 2228 self.port = self.path = self.query = self.fragment = None
2232 2229 self._localpath = True
2233 2230 self._hostport = ''
2234 2231 self._origpath = path
2235 2232
2236 2233 if parsefragment and '#' in path:
2237 2234 path, self.fragment = path.split('#', 1)
2238 2235 if not path:
2239 2236 path = None
2240 2237
2241 2238 # special case for Windows drive letters and UNC paths
2242 2239 if hasdriveletter(path) or path.startswith(r'\\'):
2243 2240 self.path = path
2244 2241 return
2245 2242
2246 2243 # For compatibility reasons, we can't handle bundle paths as
2247 2244 # normal URLS
2248 2245 if path.startswith('bundle:'):
2249 2246 self.scheme = 'bundle'
2250 2247 path = path[7:]
2251 2248 if path.startswith('//'):
2252 2249 path = path[2:]
2253 2250 self.path = path
2254 2251 return
2255 2252
2256 2253 if self._matchscheme(path):
2257 2254 parts = path.split(':', 1)
2258 2255 if parts[0]:
2259 2256 self.scheme, path = parts
2260 2257 self._localpath = False
2261 2258
2262 2259 if not path:
2263 2260 path = None
2264 2261 if self._localpath:
2265 2262 self.path = ''
2266 2263 return
2267 2264 else:
2268 2265 if self._localpath:
2269 2266 self.path = path
2270 2267 return
2271 2268
2272 2269 if parsequery and '?' in path:
2273 2270 path, self.query = path.split('?', 1)
2274 2271 if not path:
2275 2272 path = None
2276 2273 if not self.query:
2277 2274 self.query = None
2278 2275
2279 2276 # // is required to specify a host/authority
2280 2277 if path and path.startswith('//'):
2281 2278 parts = path[2:].split('/', 1)
2282 2279 if len(parts) > 1:
2283 2280 self.host, path = parts
2284 2281 else:
2285 2282 self.host = parts[0]
2286 2283 path = None
2287 2284 if not self.host:
2288 2285 self.host = None
2289 2286 # path of file:///d is /d
2290 2287 # path of file:///d:/ is d:/, not /d:/
2291 2288 if path and not hasdriveletter(path):
2292 2289 path = '/' + path
2293 2290
2294 2291 if self.host and '@' in self.host:
2295 2292 self.user, self.host = self.host.rsplit('@', 1)
2296 2293 if ':' in self.user:
2297 2294 self.user, self.passwd = self.user.split(':', 1)
2298 2295 if not self.host:
2299 2296 self.host = None
2300 2297
2301 2298 # Don't split on colons in IPv6 addresses without ports
2302 2299 if (self.host and ':' in self.host and
2303 2300 not (self.host.startswith('[') and self.host.endswith(']'))):
2304 2301 self._hostport = self.host
2305 2302 self.host, self.port = self.host.rsplit(':', 1)
2306 2303 if not self.host:
2307 2304 self.host = None
2308 2305
2309 2306 if (self.host and self.scheme == 'file' and
2310 2307 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2311 2308 raise Abort(_('file:// URLs can only refer to localhost'))
2312 2309
2313 2310 self.path = path
2314 2311
2315 2312 # leave the query string escaped
2316 2313 for a in ('user', 'passwd', 'host', 'port',
2317 2314 'path', 'fragment'):
2318 2315 v = getattr(self, a)
2319 2316 if v is not None:
2320 2317 setattr(self, a, _urlunquote(v))
2321 2318
2322 2319 def __repr__(self):
2323 2320 attrs = []
2324 2321 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2325 2322 'query', 'fragment'):
2326 2323 v = getattr(self, a)
2327 2324 if v is not None:
2328 2325 attrs.append('%s: %r' % (a, v))
2329 2326 return '<url %s>' % ', '.join(attrs)
2330 2327
2331 2328 def __str__(self):
2332 2329 r"""Join the URL's components back into a URL string.
2333 2330
2334 2331 Examples:
2335 2332
2336 2333 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2337 2334 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2338 2335 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2339 2336 'http://user:pw@host:80/?foo=bar&baz=42'
2340 2337 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2341 2338 'http://user:pw@host:80/?foo=bar%3dbaz'
2342 2339 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2343 2340 'ssh://user:pw@[::1]:2200//home/joe#'
2344 2341 >>> str(url('http://localhost:80//'))
2345 2342 'http://localhost:80//'
2346 2343 >>> str(url('http://localhost:80/'))
2347 2344 'http://localhost:80/'
2348 2345 >>> str(url('http://localhost:80'))
2349 2346 'http://localhost:80/'
2350 2347 >>> str(url('bundle:foo'))
2351 2348 'bundle:foo'
2352 2349 >>> str(url('bundle://../foo'))
2353 2350 'bundle:../foo'
2354 2351 >>> str(url('path'))
2355 2352 'path'
2356 2353 >>> str(url('file:///tmp/foo/bar'))
2357 2354 'file:///tmp/foo/bar'
2358 2355 >>> str(url('file:///c:/tmp/foo/bar'))
2359 2356 'file:///c:/tmp/foo/bar'
2360 2357 >>> print url(r'bundle:foo\bar')
2361 2358 bundle:foo\bar
2362 2359 >>> print url(r'file:///D:\data\hg')
2363 2360 file:///D:\data\hg
2364 2361 """
2365 2362 if self._localpath:
2366 2363 s = self.path
2367 2364 if self.scheme == 'bundle':
2368 2365 s = 'bundle:' + s
2369 2366 if self.fragment:
2370 2367 s += '#' + self.fragment
2371 2368 return s
2372 2369
2373 2370 s = self.scheme + ':'
2374 2371 if self.user or self.passwd or self.host:
2375 2372 s += '//'
2376 2373 elif self.scheme and (not self.path or self.path.startswith('/')
2377 2374 or hasdriveletter(self.path)):
2378 2375 s += '//'
2379 2376 if hasdriveletter(self.path):
2380 2377 s += '/'
2381 2378 if self.user:
2382 2379 s += urllib.quote(self.user, safe=self._safechars)
2383 2380 if self.passwd:
2384 2381 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
2385 2382 if self.user or self.passwd:
2386 2383 s += '@'
2387 2384 if self.host:
2388 2385 if not (self.host.startswith('[') and self.host.endswith(']')):
2389 2386 s += urllib.quote(self.host)
2390 2387 else:
2391 2388 s += self.host
2392 2389 if self.port:
2393 2390 s += ':' + urllib.quote(self.port)
2394 2391 if self.host:
2395 2392 s += '/'
2396 2393 if self.path:
2397 2394 # TODO: similar to the query string, we should not unescape the
2398 2395 # path when we store it, the path might contain '%2f' = '/',
2399 2396 # which we should *not* escape.
2400 2397 s += urllib.quote(self.path, safe=self._safepchars)
2401 2398 if self.query:
2402 2399 # we store the query in escaped form.
2403 2400 s += '?' + self.query
2404 2401 if self.fragment is not None:
2405 2402 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
2406 2403 return s
2407 2404
2408 2405 def authinfo(self):
2409 2406 user, passwd = self.user, self.passwd
2410 2407 try:
2411 2408 self.user, self.passwd = None, None
2412 2409 s = str(self)
2413 2410 finally:
2414 2411 self.user, self.passwd = user, passwd
2415 2412 if not self.user:
2416 2413 return (s, None)
2417 2414 # authinfo[1] is passed to urllib2 password manager, and its
2418 2415 # URIs must not contain credentials. The host is passed in the
2419 2416 # URIs list because Python < 2.4.3 uses only that to search for
2420 2417 # a password.
2421 2418 return (s, (None, (s, self.host),
2422 2419 self.user, self.passwd or ''))
2423 2420
2424 2421 def isabs(self):
2425 2422 if self.scheme and self.scheme != 'file':
2426 2423 return True # remote URL
2427 2424 if hasdriveletter(self.path):
2428 2425 return True # absolute for our purposes - can't be joined()
2429 2426 if self.path.startswith(r'\\'):
2430 2427 return True # Windows UNC path
2431 2428 if self.path.startswith('/'):
2432 2429 return True # POSIX-style
2433 2430 return False
2434 2431
2435 2432 def localpath(self):
2436 2433 if self.scheme == 'file' or self.scheme == 'bundle':
2437 2434 path = self.path or '/'
2438 2435 # For Windows, we need to promote hosts containing drive
2439 2436 # letters to paths with drive letters.
2440 2437 if hasdriveletter(self._hostport):
2441 2438 path = self._hostport + '/' + self.path
2442 2439 elif (self.host is not None and self.path
2443 2440 and not hasdriveletter(path)):
2444 2441 path = '/' + path
2445 2442 return path
2446 2443 return self._origpath
2447 2444
2448 2445 def islocal(self):
2449 2446 '''whether localpath will return something that posixfile can open'''
2450 2447 return (not self.scheme or self.scheme == 'file'
2451 2448 or self.scheme == 'bundle')
2452 2449
2453 2450 def hasscheme(path):
2454 2451 return bool(url(path).scheme)
2455 2452
2456 2453 def hasdriveletter(path):
2457 2454 return path and path[1:2] == ':' and path[0:1].isalpha()
2458 2455
2459 2456 def urllocalpath(path):
2460 2457 return url(path, parsequery=False, parsefragment=False).localpath()
2461 2458
2462 2459 def hidepassword(u):
2463 2460 '''hide user credential in a url string'''
2464 2461 u = url(u)
2465 2462 if u.passwd:
2466 2463 u.passwd = '***'
2467 2464 return str(u)
2468 2465
2469 2466 def removeauth(u):
2470 2467 '''remove all authentication information from a url string'''
2471 2468 u = url(u)
2472 2469 u.user = u.passwd = None
2473 2470 return str(u)
2474 2471
2475 2472 def isatty(fp):
2476 2473 try:
2477 2474 return fp.isatty()
2478 2475 except AttributeError:
2479 2476 return False
2480 2477
2481 2478 timecount = unitcountfn(
2482 2479 (1, 1e3, _('%.0f s')),
2483 2480 (100, 1, _('%.1f s')),
2484 2481 (10, 1, _('%.2f s')),
2485 2482 (1, 1, _('%.3f s')),
2486 2483 (100, 0.001, _('%.1f ms')),
2487 2484 (10, 0.001, _('%.2f ms')),
2488 2485 (1, 0.001, _('%.3f ms')),
2489 2486 (100, 0.000001, _('%.1f us')),
2490 2487 (10, 0.000001, _('%.2f us')),
2491 2488 (1, 0.000001, _('%.3f us')),
2492 2489 (100, 0.000000001, _('%.1f ns')),
2493 2490 (10, 0.000000001, _('%.2f ns')),
2494 2491 (1, 0.000000001, _('%.3f ns')),
2495 2492 )
2496 2493
2497 2494 _timenesting = [0]
2498 2495
2499 2496 def timed(func):
2500 2497 '''Report the execution time of a function call to stderr.
2501 2498
2502 2499 During development, use as a decorator when you need to measure
2503 2500 the cost of a function, e.g. as follows:
2504 2501
2505 2502 @util.timed
2506 2503 def foo(a, b, c):
2507 2504 pass
2508 2505 '''
2509 2506
2510 2507 def wrapper(*args, **kwargs):
2511 2508 start = time.time()
2512 2509 indent = 2
2513 2510 _timenesting[0] += indent
2514 2511 try:
2515 2512 return func(*args, **kwargs)
2516 2513 finally:
2517 2514 elapsed = time.time() - start
2518 2515 _timenesting[0] -= indent
2519 2516 sys.stderr.write('%s%s: %s\n' %
2520 2517 (' ' * _timenesting[0], func.__name__,
2521 2518 timecount(elapsed)))
2522 2519 return wrapper
2523 2520
2524 2521 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2525 2522 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2526 2523
2527 2524 def sizetoint(s):
2528 2525 '''Convert a space specifier to a byte count.
2529 2526
2530 2527 >>> sizetoint('30')
2531 2528 30
2532 2529 >>> sizetoint('2.2kb')
2533 2530 2252
2534 2531 >>> sizetoint('6M')
2535 2532 6291456
2536 2533 '''
2537 2534 t = s.strip().lower()
2538 2535 try:
2539 2536 for k, u in _sizeunits:
2540 2537 if t.endswith(k):
2541 2538 return int(float(t[:-len(k)]) * u)
2542 2539 return int(t)
2543 2540 except ValueError:
2544 2541 raise error.ParseError(_("couldn't parse size: %s") % s)
2545 2542
2546 2543 class hooks(object):
2547 2544 '''A collection of hook functions that can be used to extend a
2548 2545 function's behavior. Hooks are called in lexicographic order,
2549 2546 based on the names of their sources.'''
2550 2547
2551 2548 def __init__(self):
2552 2549 self._hooks = []
2553 2550
2554 2551 def add(self, source, hook):
2555 2552 self._hooks.append((source, hook))
2556 2553
2557 2554 def __call__(self, *args):
2558 2555 self._hooks.sort(key=lambda x: x[0])
2559 2556 results = []
2560 2557 for source, hook in self._hooks:
2561 2558 results.append(hook(*args))
2562 2559 return results
2563 2560
2564 2561 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2565 2562 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2566 2563 Skips the 'skip' last entries. By default it will flush stdout first.
2567 2564 It can be used everywhere and do intentionally not require an ui object.
2568 2565 Not be used in production code but very convenient while developing.
2569 2566 '''
2570 2567 if otherf:
2571 2568 otherf.flush()
2572 2569 f.write('%s at:\n' % msg)
2573 2570 entries = [('%s:%s' % (fn, ln), func)
2574 2571 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2575 2572 if entries:
2576 2573 fnmax = max(len(entry[0]) for entry in entries)
2577 2574 for fnln, func in entries:
2578 2575 f.write(' %-*s in %s\n' % (fnmax, fnln, func))
2579 2576 f.flush()
2580 2577
2581 2578 class dirs(object):
2582 2579 '''a multiset of directory names from a dirstate or manifest'''
2583 2580
2584 2581 def __init__(self, map, skip=None):
2585 2582 self._dirs = {}
2586 2583 addpath = self.addpath
2587 2584 if safehasattr(map, 'iteritems') and skip is not None:
2588 2585 for f, s in map.iteritems():
2589 2586 if s[0] != skip:
2590 2587 addpath(f)
2591 2588 else:
2592 2589 for f in map:
2593 2590 addpath(f)
2594 2591
2595 2592 def addpath(self, path):
2596 2593 dirs = self._dirs
2597 2594 for base in finddirs(path):
2598 2595 if base in dirs:
2599 2596 dirs[base] += 1
2600 2597 return
2601 2598 dirs[base] = 1
2602 2599
2603 2600 def delpath(self, path):
2604 2601 dirs = self._dirs
2605 2602 for base in finddirs(path):
2606 2603 if dirs[base] > 1:
2607 2604 dirs[base] -= 1
2608 2605 return
2609 2606 del dirs[base]
2610 2607
2611 2608 def __iter__(self):
2612 2609 return self._dirs.iterkeys()
2613 2610
2614 2611 def __contains__(self, d):
2615 2612 return d in self._dirs
2616 2613
2617 2614 if safehasattr(parsers, 'dirs'):
2618 2615 dirs = parsers.dirs
2619 2616
2620 2617 def finddirs(path):
2621 2618 pos = path.rfind('/')
2622 2619 while pos != -1:
2623 2620 yield path[:pos]
2624 2621 pos = path.rfind('/', 0, pos)
2625 2622
2626 2623 # compression utility
2627 2624
2628 2625 class nocompress(object):
2629 2626 def compress(self, x):
2630 2627 return x
2631 2628 def flush(self):
2632 2629 return ""
2633 2630
2634 2631 compressors = {
2635 2632 None: nocompress,
2636 2633 # lambda to prevent early import
2637 2634 'BZ': lambda: bz2.BZ2Compressor(),
2638 2635 'GZ': lambda: zlib.compressobj(),
2639 2636 }
2640 2637 # also support the old form by courtesies
2641 2638 compressors['UN'] = compressors[None]
2642 2639
2643 2640 def _makedecompressor(decompcls):
2644 2641 def generator(f):
2645 2642 d = decompcls()
2646 2643 for chunk in filechunkiter(f):
2647 2644 yield d.decompress(chunk)
2648 2645 def func(fh):
2649 2646 return chunkbuffer(generator(fh))
2650 2647 return func
2651 2648
2652 2649 class ctxmanager(object):
2653 2650 '''A context manager for use in 'with' blocks to allow multiple
2654 2651 contexts to be entered at once. This is both safer and more
2655 2652 flexible than contextlib.nested.
2656 2653
2657 2654 Once Mercurial supports Python 2.7+, this will become mostly
2658 2655 unnecessary.
2659 2656 '''
2660 2657
2661 2658 def __init__(self, *args):
2662 2659 '''Accepts a list of no-argument functions that return context
2663 2660 managers. These will be invoked at __call__ time.'''
2664 2661 self._pending = args
2665 2662 self._atexit = []
2666 2663
2667 2664 def __enter__(self):
2668 2665 return self
2669 2666
2670 2667 def __call__(self):
2671 2668 '''Create and enter context managers in the order in which they were
2672 2669 passed to the constructor.'''
2673 2670 values = []
2674 2671 for func in self._pending:
2675 2672 obj = func()
2676 2673 values.append(obj.__enter__())
2677 2674 self._atexit.append(obj.__exit__)
2678 2675 del self._pending
2679 2676 return values
2680 2677
2681 2678 def atexit(self, func, *args, **kwargs):
2682 2679 '''Add a function to call when this context manager exits. The
2683 2680 ordering of multiple atexit calls is unspecified, save that
2684 2681 they will happen before any __exit__ functions.'''
2685 2682 def wrapper(exc_type, exc_val, exc_tb):
2686 2683 func(*args, **kwargs)
2687 2684 self._atexit.append(wrapper)
2688 2685 return func
2689 2686
2690 2687 def __exit__(self, exc_type, exc_val, exc_tb):
2691 2688 '''Context managers are exited in the reverse order from which
2692 2689 they were created.'''
2693 2690 received = exc_type is not None
2694 2691 suppressed = False
2695 2692 pending = None
2696 2693 self._atexit.reverse()
2697 2694 for exitfunc in self._atexit:
2698 2695 try:
2699 2696 if exitfunc(exc_type, exc_val, exc_tb):
2700 2697 suppressed = True
2701 2698 exc_type = None
2702 2699 exc_val = None
2703 2700 exc_tb = None
2704 2701 except BaseException:
2705 2702 pending = sys.exc_info()
2706 2703 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2707 2704 del self._atexit
2708 2705 if pending:
2709 2706 raise exc_val
2710 2707 return received and suppressed
2711 2708
2712 2709 def _bz2():
2713 2710 d = bz2.BZ2Decompressor()
2714 2711 # Bzip2 stream start with BZ, but we stripped it.
2715 2712 # we put it back for good measure.
2716 2713 d.decompress('BZ')
2717 2714 return d
2718 2715
2719 2716 decompressors = {None: lambda fh: fh,
2720 2717 '_truncatedBZ': _makedecompressor(_bz2),
2721 2718 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2722 2719 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2723 2720 }
2724 2721 # also support the old form by courtesies
2725 2722 decompressors['UN'] = decompressors[None]
2726 2723
2727 2724 # convenient shortcut
2728 2725 dst = debugstacktrace
General Comments 0
You need to be logged in to leave comments. Login now