##// END OF EJS Templates
util: improved the check for the existence of the 'buffer' builtin...
Renato Cunha -
r11565:7546d4a2 default
parent child Browse files
Show More
@@ -1,1390 +1,1392 b''
1 1 # util.py - Mercurial utility functions and platform specfic 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 specfic 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 i18n import _
17 17 import error, osutil, encoding
18 18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 try:
32 32 from hashlib import sha1 as _sha1
33 33 except ImportError:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 def fakebuffer(sliceable, offset=0):
42 42 return sliceable[offset:]
43 if not hasattr(__builtin__, 'buffer'):
43 try:
44 buffer
45 except NameError:
44 46 __builtin__.buffer = fakebuffer
45 47
46 48 import subprocess
47 49 closefds = os.name == 'posix'
48 50
49 51 def popen2(cmd, env=None, newlines=False):
50 52 # Setting bufsize to -1 lets the system decide the buffer size.
51 53 # The default for bufsize is 0, meaning unbuffered. This leads to
52 54 # poor performance on Mac OS X: http://bugs.python.org/issue4194
53 55 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
54 56 close_fds=closefds,
55 57 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
56 58 universal_newlines=newlines,
57 59 env=env)
58 60 return p.stdin, p.stdout
59 61
60 62 def popen3(cmd, env=None, newlines=False):
61 63 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
62 64 close_fds=closefds,
63 65 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 66 stderr=subprocess.PIPE,
65 67 universal_newlines=newlines,
66 68 env=env)
67 69 return p.stdin, p.stdout, p.stderr
68 70
69 71 def version():
70 72 """Return version information if available."""
71 73 try:
72 74 import __version__
73 75 return __version__.version
74 76 except ImportError:
75 77 return 'unknown'
76 78
77 79 # used by parsedate
78 80 defaultdateformats = (
79 81 '%Y-%m-%d %H:%M:%S',
80 82 '%Y-%m-%d %I:%M:%S%p',
81 83 '%Y-%m-%d %H:%M',
82 84 '%Y-%m-%d %I:%M%p',
83 85 '%Y-%m-%d',
84 86 '%m-%d',
85 87 '%m/%d',
86 88 '%m/%d/%y',
87 89 '%m/%d/%Y',
88 90 '%a %b %d %H:%M:%S %Y',
89 91 '%a %b %d %I:%M:%S%p %Y',
90 92 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
91 93 '%b %d %H:%M:%S %Y',
92 94 '%b %d %I:%M:%S%p %Y',
93 95 '%b %d %H:%M:%S',
94 96 '%b %d %I:%M:%S%p',
95 97 '%b %d %H:%M',
96 98 '%b %d %I:%M%p',
97 99 '%b %d %Y',
98 100 '%b %d',
99 101 '%H:%M:%S',
100 102 '%I:%M:%S%p',
101 103 '%H:%M',
102 104 '%I:%M%p',
103 105 )
104 106
105 107 extendeddateformats = defaultdateformats + (
106 108 "%Y",
107 109 "%Y-%m",
108 110 "%b",
109 111 "%b %Y",
110 112 )
111 113
112 114 def cachefunc(func):
113 115 '''cache the result of function calls'''
114 116 # XXX doesn't handle keywords args
115 117 cache = {}
116 118 if func.func_code.co_argcount == 1:
117 119 # we gain a small amount of time because
118 120 # we don't need to pack/unpack the list
119 121 def f(arg):
120 122 if arg not in cache:
121 123 cache[arg] = func(arg)
122 124 return cache[arg]
123 125 else:
124 126 def f(*args):
125 127 if args not in cache:
126 128 cache[args] = func(*args)
127 129 return cache[args]
128 130
129 131 return f
130 132
131 133 def lrucachefunc(func):
132 134 '''cache most recent results of function calls'''
133 135 cache = {}
134 136 order = []
135 137 if func.func_code.co_argcount == 1:
136 138 def f(arg):
137 139 if arg not in cache:
138 140 if len(cache) > 20:
139 141 del cache[order.pop(0)]
140 142 cache[arg] = func(arg)
141 143 else:
142 144 order.remove(arg)
143 145 order.append(arg)
144 146 return cache[arg]
145 147 else:
146 148 def f(*args):
147 149 if args not in cache:
148 150 if len(cache) > 20:
149 151 del cache[order.pop(0)]
150 152 cache[args] = func(*args)
151 153 else:
152 154 order.remove(args)
153 155 order.append(args)
154 156 return cache[args]
155 157
156 158 return f
157 159
158 160 class propertycache(object):
159 161 def __init__(self, func):
160 162 self.func = func
161 163 self.name = func.__name__
162 164 def __get__(self, obj, type=None):
163 165 result = self.func(obj)
164 166 setattr(obj, self.name, result)
165 167 return result
166 168
167 169 def pipefilter(s, cmd):
168 170 '''filter string S through command CMD, returning its output'''
169 171 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
170 172 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
171 173 pout, perr = p.communicate(s)
172 174 return pout
173 175
174 176 def tempfilter(s, cmd):
175 177 '''filter string S through a pair of temporary files with CMD.
176 178 CMD is used as a template to create the real command to be run,
177 179 with the strings INFILE and OUTFILE replaced by the real names of
178 180 the temporary files generated.'''
179 181 inname, outname = None, None
180 182 try:
181 183 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
182 184 fp = os.fdopen(infd, 'wb')
183 185 fp.write(s)
184 186 fp.close()
185 187 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
186 188 os.close(outfd)
187 189 cmd = cmd.replace('INFILE', inname)
188 190 cmd = cmd.replace('OUTFILE', outname)
189 191 code = os.system(cmd)
190 192 if sys.platform == 'OpenVMS' and code & 1:
191 193 code = 0
192 194 if code:
193 195 raise Abort(_("command '%s' failed: %s") %
194 196 (cmd, explain_exit(code)))
195 197 return open(outname, 'rb').read()
196 198 finally:
197 199 try:
198 200 if inname:
199 201 os.unlink(inname)
200 202 except:
201 203 pass
202 204 try:
203 205 if outname:
204 206 os.unlink(outname)
205 207 except:
206 208 pass
207 209
208 210 filtertable = {
209 211 'tempfile:': tempfilter,
210 212 'pipe:': pipefilter,
211 213 }
212 214
213 215 def filter(s, cmd):
214 216 "filter a string through a command that transforms its input to its output"
215 217 for name, fn in filtertable.iteritems():
216 218 if cmd.startswith(name):
217 219 return fn(s, cmd[len(name):].lstrip())
218 220 return pipefilter(s, cmd)
219 221
220 222 def binary(s):
221 223 """return true if a string is binary data"""
222 224 return bool(s and '\0' in s)
223 225
224 226 def increasingchunks(source, min=1024, max=65536):
225 227 '''return no less than min bytes per chunk while data remains,
226 228 doubling min after each chunk until it reaches max'''
227 229 def log2(x):
228 230 if not x:
229 231 return 0
230 232 i = 0
231 233 while x:
232 234 x >>= 1
233 235 i += 1
234 236 return i - 1
235 237
236 238 buf = []
237 239 blen = 0
238 240 for chunk in source:
239 241 buf.append(chunk)
240 242 blen += len(chunk)
241 243 if blen >= min:
242 244 if min < max:
243 245 min = min << 1
244 246 nmin = 1 << log2(blen)
245 247 if nmin > min:
246 248 min = nmin
247 249 if min > max:
248 250 min = max
249 251 yield ''.join(buf)
250 252 blen = 0
251 253 buf = []
252 254 if buf:
253 255 yield ''.join(buf)
254 256
255 257 Abort = error.Abort
256 258
257 259 def always(fn):
258 260 return True
259 261
260 262 def never(fn):
261 263 return False
262 264
263 265 def pathto(root, n1, n2):
264 266 '''return the relative path from one place to another.
265 267 root should use os.sep to separate directories
266 268 n1 should use os.sep to separate directories
267 269 n2 should use "/" to separate directories
268 270 returns an os.sep-separated path.
269 271
270 272 If n1 is a relative path, it's assumed it's
271 273 relative to root.
272 274 n2 should always be relative to root.
273 275 '''
274 276 if not n1:
275 277 return localpath(n2)
276 278 if os.path.isabs(n1):
277 279 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
278 280 return os.path.join(root, localpath(n2))
279 281 n2 = '/'.join((pconvert(root), n2))
280 282 a, b = splitpath(n1), n2.split('/')
281 283 a.reverse()
282 284 b.reverse()
283 285 while a and b and a[-1] == b[-1]:
284 286 a.pop()
285 287 b.pop()
286 288 b.reverse()
287 289 return os.sep.join((['..'] * len(a)) + b) or '.'
288 290
289 291 def canonpath(root, cwd, myname):
290 292 """return the canonical path of myname, given cwd and root"""
291 293 if endswithsep(root):
292 294 rootsep = root
293 295 else:
294 296 rootsep = root + os.sep
295 297 name = myname
296 298 if not os.path.isabs(name):
297 299 name = os.path.join(root, cwd, name)
298 300 name = os.path.normpath(name)
299 301 audit_path = path_auditor(root)
300 302 if name != rootsep and name.startswith(rootsep):
301 303 name = name[len(rootsep):]
302 304 audit_path(name)
303 305 return pconvert(name)
304 306 elif name == root:
305 307 return ''
306 308 else:
307 309 # Determine whether `name' is in the hierarchy at or beneath `root',
308 310 # by iterating name=dirname(name) until that causes no change (can't
309 311 # check name == '/', because that doesn't work on windows). For each
310 312 # `name', compare dev/inode numbers. If they match, the list `rel'
311 313 # holds the reversed list of components making up the relative file
312 314 # name we want.
313 315 root_st = os.stat(root)
314 316 rel = []
315 317 while True:
316 318 try:
317 319 name_st = os.stat(name)
318 320 except OSError:
319 321 break
320 322 if samestat(name_st, root_st):
321 323 if not rel:
322 324 # name was actually the same as root (maybe a symlink)
323 325 return ''
324 326 rel.reverse()
325 327 name = os.path.join(*rel)
326 328 audit_path(name)
327 329 return pconvert(name)
328 330 dirname, basename = os.path.split(name)
329 331 rel.append(basename)
330 332 if dirname == name:
331 333 break
332 334 name = dirname
333 335
334 336 raise Abort('%s not under root' % myname)
335 337
336 338 _hgexecutable = None
337 339
338 340 def main_is_frozen():
339 341 """return True if we are a frozen executable.
340 342
341 343 The code supports py2exe (most common, Windows only) and tools/freeze
342 344 (portable, not much used).
343 345 """
344 346 return (hasattr(sys, "frozen") or # new py2exe
345 347 hasattr(sys, "importers") or # old py2exe
346 348 imp.is_frozen("__main__")) # tools/freeze
347 349
348 350 def hgexecutable():
349 351 """return location of the 'hg' executable.
350 352
351 353 Defaults to $HG or 'hg' in the search path.
352 354 """
353 355 if _hgexecutable is None:
354 356 hg = os.environ.get('HG')
355 357 if hg:
356 358 set_hgexecutable(hg)
357 359 elif main_is_frozen():
358 360 set_hgexecutable(sys.executable)
359 361 else:
360 362 exe = find_exe('hg') or os.path.basename(sys.argv[0])
361 363 set_hgexecutable(exe)
362 364 return _hgexecutable
363 365
364 366 def set_hgexecutable(path):
365 367 """set location of the 'hg' executable"""
366 368 global _hgexecutable
367 369 _hgexecutable = path
368 370
369 371 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
370 372 '''enhanced shell command execution.
371 373 run with environment maybe modified, maybe in different dir.
372 374
373 375 if command fails and onerr is None, return status. if ui object,
374 376 print error message and return status, else raise onerr object as
375 377 exception.
376 378
377 379 if out is specified, it is assumed to be a file-like object that has a
378 380 write() method. stdout and stderr will be redirected to out.'''
379 381 def py2shell(val):
380 382 'convert python object into string that is useful to shell'
381 383 if val is None or val is False:
382 384 return '0'
383 385 if val is True:
384 386 return '1'
385 387 return str(val)
386 388 origcmd = cmd
387 389 if os.name == 'nt':
388 390 cmd = '"%s"' % cmd
389 391 env = dict(os.environ)
390 392 env.update((k, py2shell(v)) for k, v in environ.iteritems())
391 393 env['HG'] = hgexecutable()
392 394 if out is None:
393 395 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
394 396 env=env, cwd=cwd)
395 397 else:
396 398 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
397 399 env=env, cwd=cwd, stdout=subprocess.PIPE,
398 400 stderr=subprocess.STDOUT)
399 401 for line in proc.stdout:
400 402 out.write(line)
401 403 proc.wait()
402 404 rc = proc.returncode
403 405 if sys.platform == 'OpenVMS' and rc & 1:
404 406 rc = 0
405 407 if rc and onerr:
406 408 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
407 409 explain_exit(rc)[0])
408 410 if errprefix:
409 411 errmsg = '%s: %s' % (errprefix, errmsg)
410 412 try:
411 413 onerr.warn(errmsg + '\n')
412 414 except AttributeError:
413 415 raise onerr(errmsg)
414 416 return rc
415 417
416 418 def checksignature(func):
417 419 '''wrap a function with code to check for calling errors'''
418 420 def check(*args, **kwargs):
419 421 try:
420 422 return func(*args, **kwargs)
421 423 except TypeError:
422 424 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
423 425 raise error.SignatureError
424 426 raise
425 427
426 428 return check
427 429
428 430 # os.path.lexists is not available on python2.3
429 431 def lexists(filename):
430 432 "test whether a file with this name exists. does not follow symlinks"
431 433 try:
432 434 os.lstat(filename)
433 435 except:
434 436 return False
435 437 return True
436 438
437 439 def unlink(f):
438 440 """unlink and remove the directory if it is empty"""
439 441 os.unlink(f)
440 442 # try removing directories that might now be empty
441 443 try:
442 444 os.removedirs(os.path.dirname(f))
443 445 except OSError:
444 446 pass
445 447
446 448 def copyfile(src, dest):
447 449 "copy a file, preserving mode and atime/mtime"
448 450 if os.path.islink(src):
449 451 try:
450 452 os.unlink(dest)
451 453 except:
452 454 pass
453 455 os.symlink(os.readlink(src), dest)
454 456 else:
455 457 try:
456 458 shutil.copyfile(src, dest)
457 459 shutil.copystat(src, dest)
458 460 except shutil.Error, inst:
459 461 raise Abort(str(inst))
460 462
461 463 def copyfiles(src, dst, hardlink=None):
462 464 """Copy a directory tree using hardlinks if possible"""
463 465
464 466 if hardlink is None:
465 467 hardlink = (os.stat(src).st_dev ==
466 468 os.stat(os.path.dirname(dst)).st_dev)
467 469
468 470 num = 0
469 471 if os.path.isdir(src):
470 472 os.mkdir(dst)
471 473 for name, kind in osutil.listdir(src):
472 474 srcname = os.path.join(src, name)
473 475 dstname = os.path.join(dst, name)
474 476 hardlink, n = copyfiles(srcname, dstname, hardlink)
475 477 num += n
476 478 else:
477 479 if hardlink:
478 480 try:
479 481 os_link(src, dst)
480 482 except (IOError, OSError):
481 483 hardlink = False
482 484 shutil.copy(src, dst)
483 485 else:
484 486 shutil.copy(src, dst)
485 487 num += 1
486 488
487 489 return hardlink, num
488 490
489 491 class path_auditor(object):
490 492 '''ensure that a filesystem path contains no banned components.
491 493 the following properties of a path are checked:
492 494
493 495 - under top-level .hg
494 496 - starts at the root of a windows drive
495 497 - contains ".."
496 498 - traverses a symlink (e.g. a/symlink_here/b)
497 499 - inside a nested repository'''
498 500
499 501 def __init__(self, root):
500 502 self.audited = set()
501 503 self.auditeddir = set()
502 504 self.root = root
503 505
504 506 def __call__(self, path):
505 507 if path in self.audited:
506 508 return
507 509 normpath = os.path.normcase(path)
508 510 parts = splitpath(normpath)
509 511 if (os.path.splitdrive(path)[0]
510 512 or parts[0].lower() in ('.hg', '.hg.', '')
511 513 or os.pardir in parts):
512 514 raise Abort(_("path contains illegal component: %s") % path)
513 515 if '.hg' in path.lower():
514 516 lparts = [p.lower() for p in parts]
515 517 for p in '.hg', '.hg.':
516 518 if p in lparts[1:]:
517 519 pos = lparts.index(p)
518 520 base = os.path.join(*parts[:pos])
519 521 raise Abort(_('path %r is inside repo %r') % (path, base))
520 522 def check(prefix):
521 523 curpath = os.path.join(self.root, prefix)
522 524 try:
523 525 st = os.lstat(curpath)
524 526 except OSError, err:
525 527 # EINVAL can be raised as invalid path syntax under win32.
526 528 # They must be ignored for patterns can be checked too.
527 529 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
528 530 raise
529 531 else:
530 532 if stat.S_ISLNK(st.st_mode):
531 533 raise Abort(_('path %r traverses symbolic link %r') %
532 534 (path, prefix))
533 535 elif (stat.S_ISDIR(st.st_mode) and
534 536 os.path.isdir(os.path.join(curpath, '.hg'))):
535 537 raise Abort(_('path %r is inside repo %r') %
536 538 (path, prefix))
537 539 parts.pop()
538 540 prefixes = []
539 541 while parts:
540 542 prefix = os.sep.join(parts)
541 543 if prefix in self.auditeddir:
542 544 break
543 545 check(prefix)
544 546 prefixes.append(prefix)
545 547 parts.pop()
546 548
547 549 self.audited.add(path)
548 550 # only add prefixes to the cache after checking everything: we don't
549 551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
550 552 self.auditeddir.update(prefixes)
551 553
552 554 def nlinks(pathname):
553 555 """Return number of hardlinks for the given file."""
554 556 return os.lstat(pathname).st_nlink
555 557
556 558 if hasattr(os, 'link'):
557 559 os_link = os.link
558 560 else:
559 561 def os_link(src, dst):
560 562 raise OSError(0, _("Hardlinks not supported"))
561 563
562 564 def lookup_reg(key, name=None, scope=None):
563 565 return None
564 566
565 567 def hidewindow():
566 568 """Hide current shell window.
567 569
568 570 Used to hide the window opened when starting asynchronous
569 571 child process under Windows, unneeded on other systems.
570 572 """
571 573 pass
572 574
573 575 if os.name == 'nt':
574 576 from windows import *
575 577 else:
576 578 from posix import *
577 579
578 580 def makelock(info, pathname):
579 581 try:
580 582 return os.symlink(info, pathname)
581 583 except OSError, why:
582 584 if why.errno == errno.EEXIST:
583 585 raise
584 586 except AttributeError: # no symlink in os
585 587 pass
586 588
587 589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
588 590 os.write(ld, info)
589 591 os.close(ld)
590 592
591 593 def readlock(pathname):
592 594 try:
593 595 return os.readlink(pathname)
594 596 except OSError, why:
595 597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
596 598 raise
597 599 except AttributeError: # no symlink in os
598 600 pass
599 601 return posixfile(pathname).read()
600 602
601 603 def fstat(fp):
602 604 '''stat file object that may not have fileno method.'''
603 605 try:
604 606 return os.fstat(fp.fileno())
605 607 except AttributeError:
606 608 return os.stat(fp.name)
607 609
608 610 # File system features
609 611
610 612 def checkcase(path):
611 613 """
612 614 Check whether the given path is on a case-sensitive filesystem
613 615
614 616 Requires a path (like /foo/.hg) ending with a foldable final
615 617 directory component.
616 618 """
617 619 s1 = os.stat(path)
618 620 d, b = os.path.split(path)
619 621 p2 = os.path.join(d, b.upper())
620 622 if path == p2:
621 623 p2 = os.path.join(d, b.lower())
622 624 try:
623 625 s2 = os.stat(p2)
624 626 if s2 == s1:
625 627 return False
626 628 return True
627 629 except:
628 630 return True
629 631
630 632 _fspathcache = {}
631 633 def fspath(name, root):
632 634 '''Get name in the case stored in the filesystem
633 635
634 636 The name is either relative to root, or it is an absolute path starting
635 637 with root. Note that this function is unnecessary, and should not be
636 638 called, for case-sensitive filesystems (simply because it's expensive).
637 639 '''
638 640 # If name is absolute, make it relative
639 641 if name.lower().startswith(root.lower()):
640 642 l = len(root)
641 643 if name[l] == os.sep or name[l] == os.altsep:
642 644 l = l + 1
643 645 name = name[l:]
644 646
645 647 if not os.path.exists(os.path.join(root, name)):
646 648 return None
647 649
648 650 seps = os.sep
649 651 if os.altsep:
650 652 seps = seps + os.altsep
651 653 # Protect backslashes. This gets silly very quickly.
652 654 seps.replace('\\','\\\\')
653 655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
654 656 dir = os.path.normcase(os.path.normpath(root))
655 657 result = []
656 658 for part, sep in pattern.findall(name):
657 659 if sep:
658 660 result.append(sep)
659 661 continue
660 662
661 663 if dir not in _fspathcache:
662 664 _fspathcache[dir] = os.listdir(dir)
663 665 contents = _fspathcache[dir]
664 666
665 667 lpart = part.lower()
666 668 lenp = len(part)
667 669 for n in contents:
668 670 if lenp == len(n) and n.lower() == lpart:
669 671 result.append(n)
670 672 break
671 673 else:
672 674 # Cannot happen, as the file exists!
673 675 result.append(part)
674 676 dir = os.path.join(dir, lpart)
675 677
676 678 return ''.join(result)
677 679
678 680 def checkexec(path):
679 681 """
680 682 Check whether the given path is on a filesystem with UNIX-like exec flags
681 683
682 684 Requires a directory (like /foo/.hg)
683 685 """
684 686
685 687 # VFAT on some Linux versions can flip mode but it doesn't persist
686 688 # a FS remount. Frequently we can detect it if files are created
687 689 # with exec bit on.
688 690
689 691 try:
690 692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
691 693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
692 694 try:
693 695 os.close(fh)
694 696 m = os.stat(fn).st_mode & 0777
695 697 new_file_has_exec = m & EXECFLAGS
696 698 os.chmod(fn, m ^ EXECFLAGS)
697 699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
698 700 finally:
699 701 os.unlink(fn)
700 702 except (IOError, OSError):
701 703 # we don't care, the user probably won't be able to commit anyway
702 704 return False
703 705 return not (new_file_has_exec or exec_flags_cannot_flip)
704 706
705 707 def checklink(path):
706 708 """check whether the given path is on a symlink-capable filesystem"""
707 709 # mktemp is not racy because symlink creation will fail if the
708 710 # file already exists
709 711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
710 712 try:
711 713 os.symlink(".", name)
712 714 os.unlink(name)
713 715 return True
714 716 except (OSError, AttributeError):
715 717 return False
716 718
717 719 def needbinarypatch():
718 720 """return True if patches should be applied in binary mode by default."""
719 721 return os.name == 'nt'
720 722
721 723 def endswithsep(path):
722 724 '''Check path ends with os.sep or os.altsep.'''
723 725 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
724 726
725 727 def splitpath(path):
726 728 '''Split path by os.sep.
727 729 Note that this function does not use os.altsep because this is
728 730 an alternative of simple "xxx.split(os.sep)".
729 731 It is recommended to use os.path.normpath() before using this
730 732 function if need.'''
731 733 return path.split(os.sep)
732 734
733 735 def gui():
734 736 '''Are we running in a GUI?'''
735 737 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
736 738
737 739 def mktempcopy(name, emptyok=False, createmode=None):
738 740 """Create a temporary file with the same contents from name
739 741
740 742 The permission bits are copied from the original file.
741 743
742 744 If the temporary file is going to be truncated immediately, you
743 745 can use emptyok=True as an optimization.
744 746
745 747 Returns the name of the temporary file.
746 748 """
747 749 d, fn = os.path.split(name)
748 750 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
749 751 os.close(fd)
750 752 # Temporary files are created with mode 0600, which is usually not
751 753 # what we want. If the original file already exists, just copy
752 754 # its mode. Otherwise, manually obey umask.
753 755 try:
754 756 st_mode = os.lstat(name).st_mode & 0777
755 757 except OSError, inst:
756 758 if inst.errno != errno.ENOENT:
757 759 raise
758 760 st_mode = createmode
759 761 if st_mode is None:
760 762 st_mode = ~umask
761 763 st_mode &= 0666
762 764 os.chmod(temp, st_mode)
763 765 if emptyok:
764 766 return temp
765 767 try:
766 768 try:
767 769 ifp = posixfile(name, "rb")
768 770 except IOError, inst:
769 771 if inst.errno == errno.ENOENT:
770 772 return temp
771 773 if not getattr(inst, 'filename', None):
772 774 inst.filename = name
773 775 raise
774 776 ofp = posixfile(temp, "wb")
775 777 for chunk in filechunkiter(ifp):
776 778 ofp.write(chunk)
777 779 ifp.close()
778 780 ofp.close()
779 781 except:
780 782 try: os.unlink(temp)
781 783 except: pass
782 784 raise
783 785 return temp
784 786
785 787 class atomictempfile(object):
786 788 """file-like object that atomically updates a file
787 789
788 790 All writes will be redirected to a temporary copy of the original
789 791 file. When rename is called, the copy is renamed to the original
790 792 name, making the changes visible.
791 793 """
792 794 def __init__(self, name, mode='w+b', createmode=None):
793 795 self.__name = name
794 796 self._fp = None
795 797 self.temp = mktempcopy(name, emptyok=('w' in mode),
796 798 createmode=createmode)
797 799 self._fp = posixfile(self.temp, mode)
798 800
799 801 def __getattr__(self, name):
800 802 return getattr(self._fp, name)
801 803
802 804 def rename(self):
803 805 if not self._fp.closed:
804 806 self._fp.close()
805 807 rename(self.temp, localpath(self.__name))
806 808
807 809 def __del__(self):
808 810 if not self._fp:
809 811 return
810 812 if not self._fp.closed:
811 813 try:
812 814 os.unlink(self.temp)
813 815 except: pass
814 816 self._fp.close()
815 817
816 818 def makedirs(name, mode=None):
817 819 """recursive directory creation with parent mode inheritance"""
818 820 try:
819 821 os.mkdir(name)
820 822 if mode is not None:
821 823 os.chmod(name, mode)
822 824 return
823 825 except OSError, err:
824 826 if err.errno == errno.EEXIST:
825 827 return
826 828 if err.errno != errno.ENOENT:
827 829 raise
828 830 parent = os.path.abspath(os.path.dirname(name))
829 831 makedirs(parent, mode)
830 832 makedirs(name, mode)
831 833
832 834 class opener(object):
833 835 """Open files relative to a base directory
834 836
835 837 This class is used to hide the details of COW semantics and
836 838 remote file access from higher level code.
837 839 """
838 840 def __init__(self, base, audit=True):
839 841 self.base = base
840 842 if audit:
841 843 self.audit_path = path_auditor(base)
842 844 else:
843 845 self.audit_path = always
844 846 self.createmode = None
845 847
846 848 @propertycache
847 849 def _can_symlink(self):
848 850 return checklink(self.base)
849 851
850 852 def _fixfilemode(self, name):
851 853 if self.createmode is None:
852 854 return
853 855 os.chmod(name, self.createmode & 0666)
854 856
855 857 def __call__(self, path, mode="r", text=False, atomictemp=False):
856 858 self.audit_path(path)
857 859 f = os.path.join(self.base, path)
858 860
859 861 if not text and "b" not in mode:
860 862 mode += "b" # for that other OS
861 863
862 864 nlink = -1
863 865 if mode not in ("r", "rb"):
864 866 try:
865 867 nlink = nlinks(f)
866 868 except OSError:
867 869 nlink = 0
868 870 d = os.path.dirname(f)
869 871 if not os.path.isdir(d):
870 872 makedirs(d, self.createmode)
871 873 if atomictemp:
872 874 return atomictempfile(f, mode, self.createmode)
873 875 if nlink > 1:
874 876 rename(mktempcopy(f), f)
875 877 fp = posixfile(f, mode)
876 878 if nlink == 0:
877 879 self._fixfilemode(f)
878 880 return fp
879 881
880 882 def symlink(self, src, dst):
881 883 self.audit_path(dst)
882 884 linkname = os.path.join(self.base, dst)
883 885 try:
884 886 os.unlink(linkname)
885 887 except OSError:
886 888 pass
887 889
888 890 dirname = os.path.dirname(linkname)
889 891 if not os.path.exists(dirname):
890 892 makedirs(dirname, self.createmode)
891 893
892 894 if self._can_symlink:
893 895 try:
894 896 os.symlink(src, linkname)
895 897 except OSError, err:
896 898 raise OSError(err.errno, _('could not symlink to %r: %s') %
897 899 (src, err.strerror), linkname)
898 900 else:
899 901 f = self(dst, "w")
900 902 f.write(src)
901 903 f.close()
902 904 self._fixfilemode(dst)
903 905
904 906 class chunkbuffer(object):
905 907 """Allow arbitrary sized chunks of data to be efficiently read from an
906 908 iterator over chunks of arbitrary size."""
907 909
908 910 def __init__(self, in_iter):
909 911 """in_iter is the iterator that's iterating over the input chunks.
910 912 targetsize is how big a buffer to try to maintain."""
911 913 self.iter = iter(in_iter)
912 914 self.buf = ''
913 915 self.targetsize = 2**16
914 916
915 917 def read(self, l):
916 918 """Read L bytes of data from the iterator of chunks of data.
917 919 Returns less than L bytes if the iterator runs dry."""
918 920 if l > len(self.buf) and self.iter:
919 921 # Clamp to a multiple of self.targetsize
920 922 targetsize = max(l, self.targetsize)
921 923 collector = cStringIO.StringIO()
922 924 collector.write(self.buf)
923 925 collected = len(self.buf)
924 926 for chunk in self.iter:
925 927 collector.write(chunk)
926 928 collected += len(chunk)
927 929 if collected >= targetsize:
928 930 break
929 931 if collected < targetsize:
930 932 self.iter = False
931 933 self.buf = collector.getvalue()
932 934 if len(self.buf) == l:
933 935 s, self.buf = str(self.buf), ''
934 936 else:
935 937 s, self.buf = self.buf[:l], buffer(self.buf, l)
936 938 return s
937 939
938 940 def filechunkiter(f, size=65536, limit=None):
939 941 """Create a generator that produces the data in the file size
940 942 (default 65536) bytes at a time, up to optional limit (default is
941 943 to read all data). Chunks may be less than size bytes if the
942 944 chunk is the last chunk in the file, or the file is a socket or
943 945 some other type of file that sometimes reads less data than is
944 946 requested."""
945 947 assert size >= 0
946 948 assert limit is None or limit >= 0
947 949 while True:
948 950 if limit is None:
949 951 nbytes = size
950 952 else:
951 953 nbytes = min(limit, size)
952 954 s = nbytes and f.read(nbytes)
953 955 if not s:
954 956 break
955 957 if limit:
956 958 limit -= len(s)
957 959 yield s
958 960
959 961 def makedate():
960 962 lt = time.localtime()
961 963 if lt[8] == 1 and time.daylight:
962 964 tz = time.altzone
963 965 else:
964 966 tz = time.timezone
965 967 return time.mktime(lt), tz
966 968
967 969 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
968 970 """represent a (unixtime, offset) tuple as a localized time.
969 971 unixtime is seconds since the epoch, and offset is the time zone's
970 972 number of seconds away from UTC. if timezone is false, do not
971 973 append time zone to string."""
972 974 t, tz = date or makedate()
973 975 if "%1" in format or "%2" in format:
974 976 sign = (tz > 0) and "-" or "+"
975 977 minutes = abs(tz) // 60
976 978 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
977 979 format = format.replace("%2", "%02d" % (minutes % 60))
978 980 s = time.strftime(format, time.gmtime(float(t) - tz))
979 981 return s
980 982
981 983 def shortdate(date=None):
982 984 """turn (timestamp, tzoff) tuple into iso 8631 date."""
983 985 return datestr(date, format='%Y-%m-%d')
984 986
985 987 def strdate(string, format, defaults=[]):
986 988 """parse a localized time string and return a (unixtime, offset) tuple.
987 989 if the string cannot be parsed, ValueError is raised."""
988 990 def timezone(string):
989 991 tz = string.split()[-1]
990 992 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
991 993 sign = (tz[0] == "+") and 1 or -1
992 994 hours = int(tz[1:3])
993 995 minutes = int(tz[3:5])
994 996 return -sign * (hours * 60 + minutes) * 60
995 997 if tz == "GMT" or tz == "UTC":
996 998 return 0
997 999 return None
998 1000
999 1001 # NOTE: unixtime = localunixtime + offset
1000 1002 offset, date = timezone(string), string
1001 1003 if offset != None:
1002 1004 date = " ".join(string.split()[:-1])
1003 1005
1004 1006 # add missing elements from defaults
1005 1007 for part in defaults:
1006 1008 found = [True for p in part if ("%"+p) in format]
1007 1009 if not found:
1008 1010 date += "@" + defaults[part]
1009 1011 format += "@%" + part[0]
1010 1012
1011 1013 timetuple = time.strptime(date, format)
1012 1014 localunixtime = int(calendar.timegm(timetuple))
1013 1015 if offset is None:
1014 1016 # local timezone
1015 1017 unixtime = int(time.mktime(timetuple))
1016 1018 offset = unixtime - localunixtime
1017 1019 else:
1018 1020 unixtime = localunixtime + offset
1019 1021 return unixtime, offset
1020 1022
1021 1023 def parsedate(date, formats=None, defaults=None):
1022 1024 """parse a localized date/time string and return a (unixtime, offset) tuple.
1023 1025
1024 1026 The date may be a "unixtime offset" string or in one of the specified
1025 1027 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1026 1028 """
1027 1029 if not date:
1028 1030 return 0, 0
1029 1031 if isinstance(date, tuple) and len(date) == 2:
1030 1032 return date
1031 1033 if not formats:
1032 1034 formats = defaultdateformats
1033 1035 date = date.strip()
1034 1036 try:
1035 1037 when, offset = map(int, date.split(' '))
1036 1038 except ValueError:
1037 1039 # fill out defaults
1038 1040 if not defaults:
1039 1041 defaults = {}
1040 1042 now = makedate()
1041 1043 for part in "d mb yY HI M S".split():
1042 1044 if part not in defaults:
1043 1045 if part[0] in "HMS":
1044 1046 defaults[part] = "00"
1045 1047 else:
1046 1048 defaults[part] = datestr(now, "%" + part[0])
1047 1049
1048 1050 for format in formats:
1049 1051 try:
1050 1052 when, offset = strdate(date, format, defaults)
1051 1053 except (ValueError, OverflowError):
1052 1054 pass
1053 1055 else:
1054 1056 break
1055 1057 else:
1056 1058 raise Abort(_('invalid date: %r ') % date)
1057 1059 # validate explicit (probably user-specified) date and
1058 1060 # time zone offset. values must fit in signed 32 bits for
1059 1061 # current 32-bit linux runtimes. timezones go from UTC-12
1060 1062 # to UTC+14
1061 1063 if abs(when) > 0x7fffffff:
1062 1064 raise Abort(_('date exceeds 32 bits: %d') % when)
1063 1065 if offset < -50400 or offset > 43200:
1064 1066 raise Abort(_('impossible time zone offset: %d') % offset)
1065 1067 return when, offset
1066 1068
1067 1069 def matchdate(date):
1068 1070 """Return a function that matches a given date match specifier
1069 1071
1070 1072 Formats include:
1071 1073
1072 1074 '{date}' match a given date to the accuracy provided
1073 1075
1074 1076 '<{date}' on or before a given date
1075 1077
1076 1078 '>{date}' on or after a given date
1077 1079
1078 1080 """
1079 1081
1080 1082 def lower(date):
1081 1083 d = dict(mb="1", d="1")
1082 1084 return parsedate(date, extendeddateformats, d)[0]
1083 1085
1084 1086 def upper(date):
1085 1087 d = dict(mb="12", HI="23", M="59", S="59")
1086 1088 for days in "31 30 29".split():
1087 1089 try:
1088 1090 d["d"] = days
1089 1091 return parsedate(date, extendeddateformats, d)[0]
1090 1092 except:
1091 1093 pass
1092 1094 d["d"] = "28"
1093 1095 return parsedate(date, extendeddateformats, d)[0]
1094 1096
1095 1097 date = date.strip()
1096 1098 if date[0] == "<":
1097 1099 when = upper(date[1:])
1098 1100 return lambda x: x <= when
1099 1101 elif date[0] == ">":
1100 1102 when = lower(date[1:])
1101 1103 return lambda x: x >= when
1102 1104 elif date[0] == "-":
1103 1105 try:
1104 1106 days = int(date[1:])
1105 1107 except ValueError:
1106 1108 raise Abort(_("invalid day spec: %s") % date[1:])
1107 1109 when = makedate()[0] - days * 3600 * 24
1108 1110 return lambda x: x >= when
1109 1111 elif " to " in date:
1110 1112 a, b = date.split(" to ")
1111 1113 start, stop = lower(a), upper(b)
1112 1114 return lambda x: x >= start and x <= stop
1113 1115 else:
1114 1116 start, stop = lower(date), upper(date)
1115 1117 return lambda x: x >= start and x <= stop
1116 1118
1117 1119 def shortuser(user):
1118 1120 """Return a short representation of a user name or email address."""
1119 1121 f = user.find('@')
1120 1122 if f >= 0:
1121 1123 user = user[:f]
1122 1124 f = user.find('<')
1123 1125 if f >= 0:
1124 1126 user = user[f + 1:]
1125 1127 f = user.find(' ')
1126 1128 if f >= 0:
1127 1129 user = user[:f]
1128 1130 f = user.find('.')
1129 1131 if f >= 0:
1130 1132 user = user[:f]
1131 1133 return user
1132 1134
1133 1135 def email(author):
1134 1136 '''get email of author.'''
1135 1137 r = author.find('>')
1136 1138 if r == -1:
1137 1139 r = None
1138 1140 return author[author.find('<') + 1:r]
1139 1141
1140 1142 def ellipsis(text, maxlength=400):
1141 1143 """Trim string to at most maxlength (default: 400) characters."""
1142 1144 if len(text) <= maxlength:
1143 1145 return text
1144 1146 else:
1145 1147 return "%s..." % (text[:maxlength - 3])
1146 1148
1147 1149 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1148 1150 '''yield every hg repository under path, recursively.'''
1149 1151 def errhandler(err):
1150 1152 if err.filename == path:
1151 1153 raise err
1152 1154 if followsym and hasattr(os.path, 'samestat'):
1153 1155 def _add_dir_if_not_there(dirlst, dirname):
1154 1156 match = False
1155 1157 samestat = os.path.samestat
1156 1158 dirstat = os.stat(dirname)
1157 1159 for lstdirstat in dirlst:
1158 1160 if samestat(dirstat, lstdirstat):
1159 1161 match = True
1160 1162 break
1161 1163 if not match:
1162 1164 dirlst.append(dirstat)
1163 1165 return not match
1164 1166 else:
1165 1167 followsym = False
1166 1168
1167 1169 if (seen_dirs is None) and followsym:
1168 1170 seen_dirs = []
1169 1171 _add_dir_if_not_there(seen_dirs, path)
1170 1172 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1171 1173 dirs.sort()
1172 1174 if '.hg' in dirs:
1173 1175 yield root # found a repository
1174 1176 qroot = os.path.join(root, '.hg', 'patches')
1175 1177 if os.path.isdir(os.path.join(qroot, '.hg')):
1176 1178 yield qroot # we have a patch queue repo here
1177 1179 if recurse:
1178 1180 # avoid recursing inside the .hg directory
1179 1181 dirs.remove('.hg')
1180 1182 else:
1181 1183 dirs[:] = [] # don't descend further
1182 1184 elif followsym:
1183 1185 newdirs = []
1184 1186 for d in dirs:
1185 1187 fname = os.path.join(root, d)
1186 1188 if _add_dir_if_not_there(seen_dirs, fname):
1187 1189 if os.path.islink(fname):
1188 1190 for hgname in walkrepos(fname, True, seen_dirs):
1189 1191 yield hgname
1190 1192 else:
1191 1193 newdirs.append(d)
1192 1194 dirs[:] = newdirs
1193 1195
1194 1196 _rcpath = None
1195 1197
1196 1198 def os_rcpath():
1197 1199 '''return default os-specific hgrc search path'''
1198 1200 path = system_rcpath()
1199 1201 path.extend(user_rcpath())
1200 1202 path = [os.path.normpath(f) for f in path]
1201 1203 return path
1202 1204
1203 1205 def rcpath():
1204 1206 '''return hgrc search path. if env var HGRCPATH is set, use it.
1205 1207 for each item in path, if directory, use files ending in .rc,
1206 1208 else use item.
1207 1209 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1208 1210 if no HGRCPATH, use default os-specific path.'''
1209 1211 global _rcpath
1210 1212 if _rcpath is None:
1211 1213 if 'HGRCPATH' in os.environ:
1212 1214 _rcpath = []
1213 1215 for p in os.environ['HGRCPATH'].split(os.pathsep):
1214 1216 if not p:
1215 1217 continue
1216 1218 p = expandpath(p)
1217 1219 if os.path.isdir(p):
1218 1220 for f, kind in osutil.listdir(p):
1219 1221 if f.endswith('.rc'):
1220 1222 _rcpath.append(os.path.join(p, f))
1221 1223 else:
1222 1224 _rcpath.append(p)
1223 1225 else:
1224 1226 _rcpath = os_rcpath()
1225 1227 return _rcpath
1226 1228
1227 1229 def bytecount(nbytes):
1228 1230 '''return byte count formatted as readable string, with units'''
1229 1231
1230 1232 units = (
1231 1233 (100, 1 << 30, _('%.0f GB')),
1232 1234 (10, 1 << 30, _('%.1f GB')),
1233 1235 (1, 1 << 30, _('%.2f GB')),
1234 1236 (100, 1 << 20, _('%.0f MB')),
1235 1237 (10, 1 << 20, _('%.1f MB')),
1236 1238 (1, 1 << 20, _('%.2f MB')),
1237 1239 (100, 1 << 10, _('%.0f KB')),
1238 1240 (10, 1 << 10, _('%.1f KB')),
1239 1241 (1, 1 << 10, _('%.2f KB')),
1240 1242 (1, 1, _('%.0f bytes')),
1241 1243 )
1242 1244
1243 1245 for multiplier, divisor, format in units:
1244 1246 if nbytes >= divisor * multiplier:
1245 1247 return format % (nbytes / float(divisor))
1246 1248 return units[-1][2] % nbytes
1247 1249
1248 1250 def drop_scheme(scheme, path):
1249 1251 sc = scheme + ':'
1250 1252 if path.startswith(sc):
1251 1253 path = path[len(sc):]
1252 1254 if path.startswith('//'):
1253 1255 if scheme == 'file':
1254 1256 i = path.find('/', 2)
1255 1257 if i == -1:
1256 1258 return ''
1257 1259 # On Windows, absolute paths are rooted at the current drive
1258 1260 # root. On POSIX they are rooted at the file system root.
1259 1261 if os.name == 'nt':
1260 1262 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1261 1263 path = os.path.join(droot, path[i + 1:])
1262 1264 else:
1263 1265 path = path[i:]
1264 1266 else:
1265 1267 path = path[2:]
1266 1268 return path
1267 1269
1268 1270 def uirepr(s):
1269 1271 # Avoid double backslash in Windows path repr()
1270 1272 return repr(s).replace('\\\\', '\\')
1271 1273
1272 1274 #### naming convention of below implementation follows 'textwrap' module
1273 1275
1274 1276 class MBTextWrapper(textwrap.TextWrapper):
1275 1277 def __init__(self, **kwargs):
1276 1278 textwrap.TextWrapper.__init__(self, **kwargs)
1277 1279
1278 1280 def _cutdown(self, str, space_left):
1279 1281 l = 0
1280 1282 ucstr = unicode(str, encoding.encoding)
1281 1283 w = unicodedata.east_asian_width
1282 1284 for i in xrange(len(ucstr)):
1283 1285 l += w(ucstr[i]) in 'WFA' and 2 or 1
1284 1286 if space_left < l:
1285 1287 return (ucstr[:i].encode(encoding.encoding),
1286 1288 ucstr[i:].encode(encoding.encoding))
1287 1289 return str, ''
1288 1290
1289 1291 # ----------------------------------------
1290 1292 # overriding of base class
1291 1293
1292 1294 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1293 1295 space_left = max(width - cur_len, 1)
1294 1296
1295 1297 if self.break_long_words:
1296 1298 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1297 1299 cur_line.append(cut)
1298 1300 reversed_chunks[-1] = res
1299 1301 elif not cur_line:
1300 1302 cur_line.append(reversed_chunks.pop())
1301 1303
1302 1304 #### naming convention of above implementation follows 'textwrap' module
1303 1305
1304 1306 def wrap(line, width=None, initindent='', hangindent=''):
1305 1307 if width is None:
1306 1308 width = termwidth() - 2
1307 1309 maxindent = max(len(hangindent), len(initindent))
1308 1310 if width <= maxindent:
1309 1311 # adjust for weird terminal size
1310 1312 width = max(78, maxindent + 1)
1311 1313 wrapper = MBTextWrapper(width=width,
1312 1314 initial_indent=initindent,
1313 1315 subsequent_indent=hangindent)
1314 1316 return wrapper.fill(line)
1315 1317
1316 1318 def iterlines(iterator):
1317 1319 for chunk in iterator:
1318 1320 for line in chunk.splitlines():
1319 1321 yield line
1320 1322
1321 1323 def expandpath(path):
1322 1324 return os.path.expanduser(os.path.expandvars(path))
1323 1325
1324 1326 def hgcmd():
1325 1327 """Return the command used to execute current hg
1326 1328
1327 1329 This is different from hgexecutable() because on Windows we want
1328 1330 to avoid things opening new shell windows like batch files, so we
1329 1331 get either the python call or current executable.
1330 1332 """
1331 1333 if main_is_frozen():
1332 1334 return [sys.executable]
1333 1335 return gethgcmd()
1334 1336
1335 1337 def rundetached(args, condfn):
1336 1338 """Execute the argument list in a detached process.
1337 1339
1338 1340 condfn is a callable which is called repeatedly and should return
1339 1341 True once the child process is known to have started successfully.
1340 1342 At this point, the child process PID is returned. If the child
1341 1343 process fails to start or finishes before condfn() evaluates to
1342 1344 True, return -1.
1343 1345 """
1344 1346 # Windows case is easier because the child process is either
1345 1347 # successfully starting and validating the condition or exiting
1346 1348 # on failure. We just poll on its PID. On Unix, if the child
1347 1349 # process fails to start, it will be left in a zombie state until
1348 1350 # the parent wait on it, which we cannot do since we expect a long
1349 1351 # running process on success. Instead we listen for SIGCHLD telling
1350 1352 # us our child process terminated.
1351 1353 terminated = set()
1352 1354 def handler(signum, frame):
1353 1355 terminated.add(os.wait())
1354 1356 prevhandler = None
1355 1357 if hasattr(signal, 'SIGCHLD'):
1356 1358 prevhandler = signal.signal(signal.SIGCHLD, handler)
1357 1359 try:
1358 1360 pid = spawndetached(args)
1359 1361 while not condfn():
1360 1362 if ((pid in terminated or not testpid(pid))
1361 1363 and not condfn()):
1362 1364 return -1
1363 1365 time.sleep(0.1)
1364 1366 return pid
1365 1367 finally:
1366 1368 if prevhandler is not None:
1367 1369 signal.signal(signal.SIGCHLD, prevhandler)
1368 1370
1369 1371 try:
1370 1372 any, all = any, all
1371 1373 except NameError:
1372 1374 def any(iterable):
1373 1375 for i in iterable:
1374 1376 if i:
1375 1377 return True
1376 1378 return False
1377 1379
1378 1380 def all(iterable):
1379 1381 for i in iterable:
1380 1382 if not i:
1381 1383 return False
1382 1384 return True
1383 1385
1384 1386 def termwidth():
1385 1387 if 'COLUMNS' in os.environ:
1386 1388 try:
1387 1389 return int(os.environ['COLUMNS'])
1388 1390 except ValueError:
1389 1391 pass
1390 1392 return termwidth_()
General Comments 0
You need to be logged in to leave comments. Login now