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