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