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