##// END OF EJS Templates
util: give appropriate default args to atomictempfile()...
Yuya Nishihara -
r11212:4d8db967 default
parent child Browse files
Show More
@@ -1,1345 +1,1345 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, 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 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):
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 def py2shell(val):
377 377 'convert python object into string that is useful to shell'
378 378 if val is None or val is False:
379 379 return '0'
380 380 if val is True:
381 381 return '1'
382 382 return str(val)
383 383 origcmd = cmd
384 384 if os.name == 'nt':
385 385 cmd = '"%s"' % cmd
386 386 env = dict(os.environ)
387 387 env.update((k, py2shell(v)) for k, v in environ.iteritems())
388 388 env['HG'] = hgexecutable()
389 389 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
390 390 env=env, cwd=cwd)
391 391 if sys.platform == 'OpenVMS' and rc & 1:
392 392 rc = 0
393 393 if rc and onerr:
394 394 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
395 395 explain_exit(rc)[0])
396 396 if errprefix:
397 397 errmsg = '%s: %s' % (errprefix, errmsg)
398 398 try:
399 399 onerr.warn(errmsg + '\n')
400 400 except AttributeError:
401 401 raise onerr(errmsg)
402 402 return rc
403 403
404 404 def checksignature(func):
405 405 '''wrap a function with code to check for calling errors'''
406 406 def check(*args, **kwargs):
407 407 try:
408 408 return func(*args, **kwargs)
409 409 except TypeError:
410 410 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
411 411 raise error.SignatureError
412 412 raise
413 413
414 414 return check
415 415
416 416 # os.path.lexists is not available on python2.3
417 417 def lexists(filename):
418 418 "test whether a file with this name exists. does not follow symlinks"
419 419 try:
420 420 os.lstat(filename)
421 421 except:
422 422 return False
423 423 return True
424 424
425 425 def unlink(f):
426 426 """unlink and remove the directory if it is empty"""
427 427 os.unlink(f)
428 428 # try removing directories that might now be empty
429 429 try:
430 430 os.removedirs(os.path.dirname(f))
431 431 except OSError:
432 432 pass
433 433
434 434 def copyfile(src, dest):
435 435 "copy a file, preserving mode and atime/mtime"
436 436 if os.path.islink(src):
437 437 try:
438 438 os.unlink(dest)
439 439 except:
440 440 pass
441 441 os.symlink(os.readlink(src), dest)
442 442 else:
443 443 try:
444 444 shutil.copyfile(src, dest)
445 445 shutil.copystat(src, dest)
446 446 except shutil.Error, inst:
447 447 raise Abort(str(inst))
448 448
449 449 def copyfiles(src, dst, hardlink=None):
450 450 """Copy a directory tree using hardlinks if possible"""
451 451
452 452 if hardlink is None:
453 453 hardlink = (os.stat(src).st_dev ==
454 454 os.stat(os.path.dirname(dst)).st_dev)
455 455
456 456 if os.path.isdir(src):
457 457 os.mkdir(dst)
458 458 for name, kind in osutil.listdir(src):
459 459 srcname = os.path.join(src, name)
460 460 dstname = os.path.join(dst, name)
461 461 copyfiles(srcname, dstname, hardlink)
462 462 else:
463 463 if hardlink:
464 464 try:
465 465 os_link(src, dst)
466 466 except (IOError, OSError):
467 467 hardlink = False
468 468 shutil.copy(src, dst)
469 469 else:
470 470 shutil.copy(src, dst)
471 471
472 472 class path_auditor(object):
473 473 '''ensure that a filesystem path contains no banned components.
474 474 the following properties of a path are checked:
475 475
476 476 - under top-level .hg
477 477 - starts at the root of a windows drive
478 478 - contains ".."
479 479 - traverses a symlink (e.g. a/symlink_here/b)
480 480 - inside a nested repository'''
481 481
482 482 def __init__(self, root):
483 483 self.audited = set()
484 484 self.auditeddir = set()
485 485 self.root = root
486 486
487 487 def __call__(self, path):
488 488 if path in self.audited:
489 489 return
490 490 normpath = os.path.normcase(path)
491 491 parts = splitpath(normpath)
492 492 if (os.path.splitdrive(path)[0]
493 493 or parts[0].lower() in ('.hg', '.hg.', '')
494 494 or os.pardir in parts):
495 495 raise Abort(_("path contains illegal component: %s") % path)
496 496 if '.hg' in path.lower():
497 497 lparts = [p.lower() for p in parts]
498 498 for p in '.hg', '.hg.':
499 499 if p in lparts[1:]:
500 500 pos = lparts.index(p)
501 501 base = os.path.join(*parts[:pos])
502 502 raise Abort(_('path %r is inside repo %r') % (path, base))
503 503 def check(prefix):
504 504 curpath = os.path.join(self.root, prefix)
505 505 try:
506 506 st = os.lstat(curpath)
507 507 except OSError, err:
508 508 # EINVAL can be raised as invalid path syntax under win32.
509 509 # They must be ignored for patterns can be checked too.
510 510 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
511 511 raise
512 512 else:
513 513 if stat.S_ISLNK(st.st_mode):
514 514 raise Abort(_('path %r traverses symbolic link %r') %
515 515 (path, prefix))
516 516 elif (stat.S_ISDIR(st.st_mode) and
517 517 os.path.isdir(os.path.join(curpath, '.hg'))):
518 518 raise Abort(_('path %r is inside repo %r') %
519 519 (path, prefix))
520 520 parts.pop()
521 521 prefixes = []
522 522 while parts:
523 523 prefix = os.sep.join(parts)
524 524 if prefix in self.auditeddir:
525 525 break
526 526 check(prefix)
527 527 prefixes.append(prefix)
528 528 parts.pop()
529 529
530 530 self.audited.add(path)
531 531 # only add prefixes to the cache after checking everything: we don't
532 532 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
533 533 self.auditeddir.update(prefixes)
534 534
535 535 def nlinks(pathname):
536 536 """Return number of hardlinks for the given file."""
537 537 return os.lstat(pathname).st_nlink
538 538
539 539 if hasattr(os, 'link'):
540 540 os_link = os.link
541 541 else:
542 542 def os_link(src, dst):
543 543 raise OSError(0, _("Hardlinks not supported"))
544 544
545 545 def lookup_reg(key, name=None, scope=None):
546 546 return None
547 547
548 548 def hidewindow():
549 549 """Hide current shell window.
550 550
551 551 Used to hide the window opened when starting asynchronous
552 552 child process under Windows, unneeded on other systems.
553 553 """
554 554 pass
555 555
556 556 if os.name == 'nt':
557 557 from windows import *
558 558 else:
559 559 from posix import *
560 560
561 561 def makelock(info, pathname):
562 562 try:
563 563 return os.symlink(info, pathname)
564 564 except OSError, why:
565 565 if why.errno == errno.EEXIST:
566 566 raise
567 567 except AttributeError: # no symlink in os
568 568 pass
569 569
570 570 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
571 571 os.write(ld, info)
572 572 os.close(ld)
573 573
574 574 def readlock(pathname):
575 575 try:
576 576 return os.readlink(pathname)
577 577 except OSError, why:
578 578 if why.errno not in (errno.EINVAL, errno.ENOSYS):
579 579 raise
580 580 except AttributeError: # no symlink in os
581 581 pass
582 582 return posixfile(pathname).read()
583 583
584 584 def fstat(fp):
585 585 '''stat file object that may not have fileno method.'''
586 586 try:
587 587 return os.fstat(fp.fileno())
588 588 except AttributeError:
589 589 return os.stat(fp.name)
590 590
591 591 # File system features
592 592
593 593 def checkcase(path):
594 594 """
595 595 Check whether the given path is on a case-sensitive filesystem
596 596
597 597 Requires a path (like /foo/.hg) ending with a foldable final
598 598 directory component.
599 599 """
600 600 s1 = os.stat(path)
601 601 d, b = os.path.split(path)
602 602 p2 = os.path.join(d, b.upper())
603 603 if path == p2:
604 604 p2 = os.path.join(d, b.lower())
605 605 try:
606 606 s2 = os.stat(p2)
607 607 if s2 == s1:
608 608 return False
609 609 return True
610 610 except:
611 611 return True
612 612
613 613 _fspathcache = {}
614 614 def fspath(name, root):
615 615 '''Get name in the case stored in the filesystem
616 616
617 617 The name is either relative to root, or it is an absolute path starting
618 618 with root. Note that this function is unnecessary, and should not be
619 619 called, for case-sensitive filesystems (simply because it's expensive).
620 620 '''
621 621 # If name is absolute, make it relative
622 622 if name.lower().startswith(root.lower()):
623 623 l = len(root)
624 624 if name[l] == os.sep or name[l] == os.altsep:
625 625 l = l + 1
626 626 name = name[l:]
627 627
628 628 if not os.path.exists(os.path.join(root, name)):
629 629 return None
630 630
631 631 seps = os.sep
632 632 if os.altsep:
633 633 seps = seps + os.altsep
634 634 # Protect backslashes. This gets silly very quickly.
635 635 seps.replace('\\','\\\\')
636 636 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
637 637 dir = os.path.normcase(os.path.normpath(root))
638 638 result = []
639 639 for part, sep in pattern.findall(name):
640 640 if sep:
641 641 result.append(sep)
642 642 continue
643 643
644 644 if dir not in _fspathcache:
645 645 _fspathcache[dir] = os.listdir(dir)
646 646 contents = _fspathcache[dir]
647 647
648 648 lpart = part.lower()
649 649 lenp = len(part)
650 650 for n in contents:
651 651 if lenp == len(n) and n.lower() == lpart:
652 652 result.append(n)
653 653 break
654 654 else:
655 655 # Cannot happen, as the file exists!
656 656 result.append(part)
657 657 dir = os.path.join(dir, lpart)
658 658
659 659 return ''.join(result)
660 660
661 661 def checkexec(path):
662 662 """
663 663 Check whether the given path is on a filesystem with UNIX-like exec flags
664 664
665 665 Requires a directory (like /foo/.hg)
666 666 """
667 667
668 668 # VFAT on some Linux versions can flip mode but it doesn't persist
669 669 # a FS remount. Frequently we can detect it if files are created
670 670 # with exec bit on.
671 671
672 672 try:
673 673 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
674 674 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
675 675 try:
676 676 os.close(fh)
677 677 m = os.stat(fn).st_mode & 0777
678 678 new_file_has_exec = m & EXECFLAGS
679 679 os.chmod(fn, m ^ EXECFLAGS)
680 680 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
681 681 finally:
682 682 os.unlink(fn)
683 683 except (IOError, OSError):
684 684 # we don't care, the user probably won't be able to commit anyway
685 685 return False
686 686 return not (new_file_has_exec or exec_flags_cannot_flip)
687 687
688 688 def checklink(path):
689 689 """check whether the given path is on a symlink-capable filesystem"""
690 690 # mktemp is not racy because symlink creation will fail if the
691 691 # file already exists
692 692 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
693 693 try:
694 694 os.symlink(".", name)
695 695 os.unlink(name)
696 696 return True
697 697 except (OSError, AttributeError):
698 698 return False
699 699
700 700 def needbinarypatch():
701 701 """return True if patches should be applied in binary mode by default."""
702 702 return os.name == 'nt'
703 703
704 704 def endswithsep(path):
705 705 '''Check path ends with os.sep or os.altsep.'''
706 706 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
707 707
708 708 def splitpath(path):
709 709 '''Split path by os.sep.
710 710 Note that this function does not use os.altsep because this is
711 711 an alternative of simple "xxx.split(os.sep)".
712 712 It is recommended to use os.path.normpath() before using this
713 713 function if need.'''
714 714 return path.split(os.sep)
715 715
716 716 def gui():
717 717 '''Are we running in a GUI?'''
718 718 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
719 719
720 720 def mktempcopy(name, emptyok=False, createmode=None):
721 721 """Create a temporary file with the same contents from name
722 722
723 723 The permission bits are copied from the original file.
724 724
725 725 If the temporary file is going to be truncated immediately, you
726 726 can use emptyok=True as an optimization.
727 727
728 728 Returns the name of the temporary file.
729 729 """
730 730 d, fn = os.path.split(name)
731 731 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
732 732 os.close(fd)
733 733 # Temporary files are created with mode 0600, which is usually not
734 734 # what we want. If the original file already exists, just copy
735 735 # its mode. Otherwise, manually obey umask.
736 736 try:
737 737 st_mode = os.lstat(name).st_mode & 0777
738 738 except OSError, inst:
739 739 if inst.errno != errno.ENOENT:
740 740 raise
741 741 st_mode = createmode
742 742 if st_mode is None:
743 743 st_mode = ~umask
744 744 st_mode &= 0666
745 745 os.chmod(temp, st_mode)
746 746 if emptyok:
747 747 return temp
748 748 try:
749 749 try:
750 750 ifp = posixfile(name, "rb")
751 751 except IOError, inst:
752 752 if inst.errno == errno.ENOENT:
753 753 return temp
754 754 if not getattr(inst, 'filename', None):
755 755 inst.filename = name
756 756 raise
757 757 ofp = posixfile(temp, "wb")
758 758 for chunk in filechunkiter(ifp):
759 759 ofp.write(chunk)
760 760 ifp.close()
761 761 ofp.close()
762 762 except:
763 763 try: os.unlink(temp)
764 764 except: pass
765 765 raise
766 766 return temp
767 767
768 768 class atomictempfile(object):
769 769 """file-like object that atomically updates a file
770 770
771 771 All writes will be redirected to a temporary copy of the original
772 772 file. When rename is called, the copy is renamed to the original
773 773 name, making the changes visible.
774 774 """
775 def __init__(self, name, mode, createmode):
775 def __init__(self, name, mode='w+b', createmode=None):
776 776 self.__name = name
777 777 self._fp = None
778 778 self.temp = mktempcopy(name, emptyok=('w' in mode),
779 779 createmode=createmode)
780 780 self._fp = posixfile(self.temp, mode)
781 781
782 782 def __getattr__(self, name):
783 783 return getattr(self._fp, name)
784 784
785 785 def rename(self):
786 786 if not self._fp.closed:
787 787 self._fp.close()
788 788 rename(self.temp, localpath(self.__name))
789 789
790 790 def __del__(self):
791 791 if not self._fp:
792 792 return
793 793 if not self._fp.closed:
794 794 try:
795 795 os.unlink(self.temp)
796 796 except: pass
797 797 self._fp.close()
798 798
799 799 def makedirs(name, mode=None):
800 800 """recursive directory creation with parent mode inheritance"""
801 801 try:
802 802 os.mkdir(name)
803 803 if mode is not None:
804 804 os.chmod(name, mode)
805 805 return
806 806 except OSError, err:
807 807 if err.errno == errno.EEXIST:
808 808 return
809 809 if err.errno != errno.ENOENT:
810 810 raise
811 811 parent = os.path.abspath(os.path.dirname(name))
812 812 makedirs(parent, mode)
813 813 makedirs(name, mode)
814 814
815 815 class opener(object):
816 816 """Open files relative to a base directory
817 817
818 818 This class is used to hide the details of COW semantics and
819 819 remote file access from higher level code.
820 820 """
821 821 def __init__(self, base, audit=True):
822 822 self.base = base
823 823 if audit:
824 824 self.audit_path = path_auditor(base)
825 825 else:
826 826 self.audit_path = always
827 827 self.createmode = None
828 828
829 829 @propertycache
830 830 def _can_symlink(self):
831 831 return checklink(self.base)
832 832
833 833 def _fixfilemode(self, name):
834 834 if self.createmode is None:
835 835 return
836 836 os.chmod(name, self.createmode & 0666)
837 837
838 838 def __call__(self, path, mode="r", text=False, atomictemp=False):
839 839 self.audit_path(path)
840 840 f = os.path.join(self.base, path)
841 841
842 842 if not text and "b" not in mode:
843 843 mode += "b" # for that other OS
844 844
845 845 nlink = -1
846 846 if mode not in ("r", "rb"):
847 847 try:
848 848 nlink = nlinks(f)
849 849 except OSError:
850 850 nlink = 0
851 851 d = os.path.dirname(f)
852 852 if not os.path.isdir(d):
853 853 makedirs(d, self.createmode)
854 854 if atomictemp:
855 855 return atomictempfile(f, mode, self.createmode)
856 856 if nlink > 1:
857 857 rename(mktempcopy(f), f)
858 858 fp = posixfile(f, mode)
859 859 if nlink == 0:
860 860 self._fixfilemode(f)
861 861 return fp
862 862
863 863 def symlink(self, src, dst):
864 864 self.audit_path(dst)
865 865 linkname = os.path.join(self.base, dst)
866 866 try:
867 867 os.unlink(linkname)
868 868 except OSError:
869 869 pass
870 870
871 871 dirname = os.path.dirname(linkname)
872 872 if not os.path.exists(dirname):
873 873 makedirs(dirname, self.createmode)
874 874
875 875 if self._can_symlink:
876 876 try:
877 877 os.symlink(src, linkname)
878 878 except OSError, err:
879 879 raise OSError(err.errno, _('could not symlink to %r: %s') %
880 880 (src, err.strerror), linkname)
881 881 else:
882 882 f = self(dst, "w")
883 883 f.write(src)
884 884 f.close()
885 885 self._fixfilemode(dst)
886 886
887 887 class chunkbuffer(object):
888 888 """Allow arbitrary sized chunks of data to be efficiently read from an
889 889 iterator over chunks of arbitrary size."""
890 890
891 891 def __init__(self, in_iter):
892 892 """in_iter is the iterator that's iterating over the input chunks.
893 893 targetsize is how big a buffer to try to maintain."""
894 894 self.iter = iter(in_iter)
895 895 self.buf = ''
896 896 self.targetsize = 2**16
897 897
898 898 def read(self, l):
899 899 """Read L bytes of data from the iterator of chunks of data.
900 900 Returns less than L bytes if the iterator runs dry."""
901 901 if l > len(self.buf) and self.iter:
902 902 # Clamp to a multiple of self.targetsize
903 903 targetsize = max(l, self.targetsize)
904 904 collector = cStringIO.StringIO()
905 905 collector.write(self.buf)
906 906 collected = len(self.buf)
907 907 for chunk in self.iter:
908 908 collector.write(chunk)
909 909 collected += len(chunk)
910 910 if collected >= targetsize:
911 911 break
912 912 if collected < targetsize:
913 913 self.iter = False
914 914 self.buf = collector.getvalue()
915 915 if len(self.buf) == l:
916 916 s, self.buf = str(self.buf), ''
917 917 else:
918 918 s, self.buf = self.buf[:l], buffer(self.buf, l)
919 919 return s
920 920
921 921 def filechunkiter(f, size=65536, limit=None):
922 922 """Create a generator that produces the data in the file size
923 923 (default 65536) bytes at a time, up to optional limit (default is
924 924 to read all data). Chunks may be less than size bytes if the
925 925 chunk is the last chunk in the file, or the file is a socket or
926 926 some other type of file that sometimes reads less data than is
927 927 requested."""
928 928 assert size >= 0
929 929 assert limit is None or limit >= 0
930 930 while True:
931 931 if limit is None:
932 932 nbytes = size
933 933 else:
934 934 nbytes = min(limit, size)
935 935 s = nbytes and f.read(nbytes)
936 936 if not s:
937 937 break
938 938 if limit:
939 939 limit -= len(s)
940 940 yield s
941 941
942 942 def makedate():
943 943 lt = time.localtime()
944 944 if lt[8] == 1 and time.daylight:
945 945 tz = time.altzone
946 946 else:
947 947 tz = time.timezone
948 948 return time.mktime(lt), tz
949 949
950 950 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
951 951 """represent a (unixtime, offset) tuple as a localized time.
952 952 unixtime is seconds since the epoch, and offset is the time zone's
953 953 number of seconds away from UTC. if timezone is false, do not
954 954 append time zone to string."""
955 955 t, tz = date or makedate()
956 956 if "%1" in format or "%2" in format:
957 957 sign = (tz > 0) and "-" or "+"
958 958 minutes = abs(tz) // 60
959 959 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
960 960 format = format.replace("%2", "%02d" % (minutes % 60))
961 961 s = time.strftime(format, time.gmtime(float(t) - tz))
962 962 return s
963 963
964 964 def shortdate(date=None):
965 965 """turn (timestamp, tzoff) tuple into iso 8631 date."""
966 966 return datestr(date, format='%Y-%m-%d')
967 967
968 968 def strdate(string, format, defaults=[]):
969 969 """parse a localized time string and return a (unixtime, offset) tuple.
970 970 if the string cannot be parsed, ValueError is raised."""
971 971 def timezone(string):
972 972 tz = string.split()[-1]
973 973 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
974 974 sign = (tz[0] == "+") and 1 or -1
975 975 hours = int(tz[1:3])
976 976 minutes = int(tz[3:5])
977 977 return -sign * (hours * 60 + minutes) * 60
978 978 if tz == "GMT" or tz == "UTC":
979 979 return 0
980 980 return None
981 981
982 982 # NOTE: unixtime = localunixtime + offset
983 983 offset, date = timezone(string), string
984 984 if offset != None:
985 985 date = " ".join(string.split()[:-1])
986 986
987 987 # add missing elements from defaults
988 988 for part in defaults:
989 989 found = [True for p in part if ("%"+p) in format]
990 990 if not found:
991 991 date += "@" + defaults[part]
992 992 format += "@%" + part[0]
993 993
994 994 timetuple = time.strptime(date, format)
995 995 localunixtime = int(calendar.timegm(timetuple))
996 996 if offset is None:
997 997 # local timezone
998 998 unixtime = int(time.mktime(timetuple))
999 999 offset = unixtime - localunixtime
1000 1000 else:
1001 1001 unixtime = localunixtime + offset
1002 1002 return unixtime, offset
1003 1003
1004 1004 def parsedate(date, formats=None, defaults=None):
1005 1005 """parse a localized date/time string and return a (unixtime, offset) tuple.
1006 1006
1007 1007 The date may be a "unixtime offset" string or in one of the specified
1008 1008 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1009 1009 """
1010 1010 if not date:
1011 1011 return 0, 0
1012 1012 if isinstance(date, tuple) and len(date) == 2:
1013 1013 return date
1014 1014 if not formats:
1015 1015 formats = defaultdateformats
1016 1016 date = date.strip()
1017 1017 try:
1018 1018 when, offset = map(int, date.split(' '))
1019 1019 except ValueError:
1020 1020 # fill out defaults
1021 1021 if not defaults:
1022 1022 defaults = {}
1023 1023 now = makedate()
1024 1024 for part in "d mb yY HI M S".split():
1025 1025 if part not in defaults:
1026 1026 if part[0] in "HMS":
1027 1027 defaults[part] = "00"
1028 1028 else:
1029 1029 defaults[part] = datestr(now, "%" + part[0])
1030 1030
1031 1031 for format in formats:
1032 1032 try:
1033 1033 when, offset = strdate(date, format, defaults)
1034 1034 except (ValueError, OverflowError):
1035 1035 pass
1036 1036 else:
1037 1037 break
1038 1038 else:
1039 1039 raise Abort(_('invalid date: %r ') % date)
1040 1040 # validate explicit (probably user-specified) date and
1041 1041 # time zone offset. values must fit in signed 32 bits for
1042 1042 # current 32-bit linux runtimes. timezones go from UTC-12
1043 1043 # to UTC+14
1044 1044 if abs(when) > 0x7fffffff:
1045 1045 raise Abort(_('date exceeds 32 bits: %d') % when)
1046 1046 if offset < -50400 or offset > 43200:
1047 1047 raise Abort(_('impossible time zone offset: %d') % offset)
1048 1048 return when, offset
1049 1049
1050 1050 def matchdate(date):
1051 1051 """Return a function that matches a given date match specifier
1052 1052
1053 1053 Formats include:
1054 1054
1055 1055 '{date}' match a given date to the accuracy provided
1056 1056
1057 1057 '<{date}' on or before a given date
1058 1058
1059 1059 '>{date}' on or after a given date
1060 1060
1061 1061 """
1062 1062
1063 1063 def lower(date):
1064 1064 d = dict(mb="1", d="1")
1065 1065 return parsedate(date, extendeddateformats, d)[0]
1066 1066
1067 1067 def upper(date):
1068 1068 d = dict(mb="12", HI="23", M="59", S="59")
1069 1069 for days in "31 30 29".split():
1070 1070 try:
1071 1071 d["d"] = days
1072 1072 return parsedate(date, extendeddateformats, d)[0]
1073 1073 except:
1074 1074 pass
1075 1075 d["d"] = "28"
1076 1076 return parsedate(date, extendeddateformats, d)[0]
1077 1077
1078 1078 date = date.strip()
1079 1079 if date[0] == "<":
1080 1080 when = upper(date[1:])
1081 1081 return lambda x: x <= when
1082 1082 elif date[0] == ">":
1083 1083 when = lower(date[1:])
1084 1084 return lambda x: x >= when
1085 1085 elif date[0] == "-":
1086 1086 try:
1087 1087 days = int(date[1:])
1088 1088 except ValueError:
1089 1089 raise Abort(_("invalid day spec: %s") % date[1:])
1090 1090 when = makedate()[0] - days * 3600 * 24
1091 1091 return lambda x: x >= when
1092 1092 elif " to " in date:
1093 1093 a, b = date.split(" to ")
1094 1094 start, stop = lower(a), upper(b)
1095 1095 return lambda x: x >= start and x <= stop
1096 1096 else:
1097 1097 start, stop = lower(date), upper(date)
1098 1098 return lambda x: x >= start and x <= stop
1099 1099
1100 1100 def shortuser(user):
1101 1101 """Return a short representation of a user name or email address."""
1102 1102 f = user.find('@')
1103 1103 if f >= 0:
1104 1104 user = user[:f]
1105 1105 f = user.find('<')
1106 1106 if f >= 0:
1107 1107 user = user[f + 1:]
1108 1108 f = user.find(' ')
1109 1109 if f >= 0:
1110 1110 user = user[:f]
1111 1111 f = user.find('.')
1112 1112 if f >= 0:
1113 1113 user = user[:f]
1114 1114 return user
1115 1115
1116 1116 def email(author):
1117 1117 '''get email of author.'''
1118 1118 r = author.find('>')
1119 1119 if r == -1:
1120 1120 r = None
1121 1121 return author[author.find('<') + 1:r]
1122 1122
1123 1123 def ellipsis(text, maxlength=400):
1124 1124 """Trim string to at most maxlength (default: 400) characters."""
1125 1125 if len(text) <= maxlength:
1126 1126 return text
1127 1127 else:
1128 1128 return "%s..." % (text[:maxlength - 3])
1129 1129
1130 1130 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1131 1131 '''yield every hg repository under path, recursively.'''
1132 1132 def errhandler(err):
1133 1133 if err.filename == path:
1134 1134 raise err
1135 1135 if followsym and hasattr(os.path, 'samestat'):
1136 1136 def _add_dir_if_not_there(dirlst, dirname):
1137 1137 match = False
1138 1138 samestat = os.path.samestat
1139 1139 dirstat = os.stat(dirname)
1140 1140 for lstdirstat in dirlst:
1141 1141 if samestat(dirstat, lstdirstat):
1142 1142 match = True
1143 1143 break
1144 1144 if not match:
1145 1145 dirlst.append(dirstat)
1146 1146 return not match
1147 1147 else:
1148 1148 followsym = False
1149 1149
1150 1150 if (seen_dirs is None) and followsym:
1151 1151 seen_dirs = []
1152 1152 _add_dir_if_not_there(seen_dirs, path)
1153 1153 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1154 1154 dirs.sort()
1155 1155 if '.hg' in dirs:
1156 1156 yield root # found a repository
1157 1157 qroot = os.path.join(root, '.hg', 'patches')
1158 1158 if os.path.isdir(os.path.join(qroot, '.hg')):
1159 1159 yield qroot # we have a patch queue repo here
1160 1160 if recurse:
1161 1161 # avoid recursing inside the .hg directory
1162 1162 dirs.remove('.hg')
1163 1163 else:
1164 1164 dirs[:] = [] # don't descend further
1165 1165 elif followsym:
1166 1166 newdirs = []
1167 1167 for d in dirs:
1168 1168 fname = os.path.join(root, d)
1169 1169 if _add_dir_if_not_there(seen_dirs, fname):
1170 1170 if os.path.islink(fname):
1171 1171 for hgname in walkrepos(fname, True, seen_dirs):
1172 1172 yield hgname
1173 1173 else:
1174 1174 newdirs.append(d)
1175 1175 dirs[:] = newdirs
1176 1176
1177 1177 _rcpath = None
1178 1178
1179 1179 def os_rcpath():
1180 1180 '''return default os-specific hgrc search path'''
1181 1181 path = system_rcpath()
1182 1182 path.extend(user_rcpath())
1183 1183 path = [os.path.normpath(f) for f in path]
1184 1184 return path
1185 1185
1186 1186 def rcpath():
1187 1187 '''return hgrc search path. if env var HGRCPATH is set, use it.
1188 1188 for each item in path, if directory, use files ending in .rc,
1189 1189 else use item.
1190 1190 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1191 1191 if no HGRCPATH, use default os-specific path.'''
1192 1192 global _rcpath
1193 1193 if _rcpath is None:
1194 1194 if 'HGRCPATH' in os.environ:
1195 1195 _rcpath = []
1196 1196 for p in os.environ['HGRCPATH'].split(os.pathsep):
1197 1197 if not p:
1198 1198 continue
1199 1199 p = expandpath(p)
1200 1200 if os.path.isdir(p):
1201 1201 for f, kind in osutil.listdir(p):
1202 1202 if f.endswith('.rc'):
1203 1203 _rcpath.append(os.path.join(p, f))
1204 1204 else:
1205 1205 _rcpath.append(p)
1206 1206 else:
1207 1207 _rcpath = os_rcpath()
1208 1208 return _rcpath
1209 1209
1210 1210 def bytecount(nbytes):
1211 1211 '''return byte count formatted as readable string, with units'''
1212 1212
1213 1213 units = (
1214 1214 (100, 1 << 30, _('%.0f GB')),
1215 1215 (10, 1 << 30, _('%.1f GB')),
1216 1216 (1, 1 << 30, _('%.2f GB')),
1217 1217 (100, 1 << 20, _('%.0f MB')),
1218 1218 (10, 1 << 20, _('%.1f MB')),
1219 1219 (1, 1 << 20, _('%.2f MB')),
1220 1220 (100, 1 << 10, _('%.0f KB')),
1221 1221 (10, 1 << 10, _('%.1f KB')),
1222 1222 (1, 1 << 10, _('%.2f KB')),
1223 1223 (1, 1, _('%.0f bytes')),
1224 1224 )
1225 1225
1226 1226 for multiplier, divisor, format in units:
1227 1227 if nbytes >= divisor * multiplier:
1228 1228 return format % (nbytes / float(divisor))
1229 1229 return units[-1][2] % nbytes
1230 1230
1231 1231 def drop_scheme(scheme, path):
1232 1232 sc = scheme + ':'
1233 1233 if path.startswith(sc):
1234 1234 path = path[len(sc):]
1235 1235 if path.startswith('//'):
1236 1236 if scheme == 'file':
1237 1237 i = path.find('/', 2)
1238 1238 if i == -1:
1239 1239 return ''
1240 1240 # On Windows, absolute paths are rooted at the current drive
1241 1241 # root. On POSIX they are rooted at the file system root.
1242 1242 if os.name == 'nt':
1243 1243 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1244 1244 path = os.path.join(droot, path[i + 1:])
1245 1245 else:
1246 1246 path = path[i:]
1247 1247 else:
1248 1248 path = path[2:]
1249 1249 return path
1250 1250
1251 1251 def uirepr(s):
1252 1252 # Avoid double backslash in Windows path repr()
1253 1253 return repr(s).replace('\\\\', '\\')
1254 1254
1255 1255 def wrap(line, hangindent, width=None):
1256 1256 if width is None:
1257 1257 width = termwidth() - 2
1258 1258 if width <= hangindent:
1259 1259 # adjust for weird terminal size
1260 1260 width = max(78, hangindent + 1)
1261 1261 padding = '\n' + ' ' * hangindent
1262 1262 # To avoid corrupting multi-byte characters in line, we must wrap
1263 1263 # a Unicode string instead of a bytestring.
1264 1264 try:
1265 1265 u = line.decode(encoding.encoding)
1266 1266 w = padding.join(textwrap.wrap(u, width=width - hangindent))
1267 1267 return w.encode(encoding.encoding)
1268 1268 except UnicodeDecodeError:
1269 1269 return padding.join(textwrap.wrap(line, width=width - hangindent))
1270 1270
1271 1271 def iterlines(iterator):
1272 1272 for chunk in iterator:
1273 1273 for line in chunk.splitlines():
1274 1274 yield line
1275 1275
1276 1276 def expandpath(path):
1277 1277 return os.path.expanduser(os.path.expandvars(path))
1278 1278
1279 1279 def hgcmd():
1280 1280 """Return the command used to execute current hg
1281 1281
1282 1282 This is different from hgexecutable() because on Windows we want
1283 1283 to avoid things opening new shell windows like batch files, so we
1284 1284 get either the python call or current executable.
1285 1285 """
1286 1286 if main_is_frozen():
1287 1287 return [sys.executable]
1288 1288 return gethgcmd()
1289 1289
1290 1290 def rundetached(args, condfn):
1291 1291 """Execute the argument list in a detached process.
1292 1292
1293 1293 condfn is a callable which is called repeatedly and should return
1294 1294 True once the child process is known to have started successfully.
1295 1295 At this point, the child process PID is returned. If the child
1296 1296 process fails to start or finishes before condfn() evaluates to
1297 1297 True, return -1.
1298 1298 """
1299 1299 # Windows case is easier because the child process is either
1300 1300 # successfully starting and validating the condition or exiting
1301 1301 # on failure. We just poll on its PID. On Unix, if the child
1302 1302 # process fails to start, it will be left in a zombie state until
1303 1303 # the parent wait on it, which we cannot do since we expect a long
1304 1304 # running process on success. Instead we listen for SIGCHLD telling
1305 1305 # us our child process terminated.
1306 1306 terminated = set()
1307 1307 def handler(signum, frame):
1308 1308 terminated.add(os.wait())
1309 1309 prevhandler = None
1310 1310 if hasattr(signal, 'SIGCHLD'):
1311 1311 prevhandler = signal.signal(signal.SIGCHLD, handler)
1312 1312 try:
1313 1313 pid = spawndetached(args)
1314 1314 while not condfn():
1315 1315 if ((pid in terminated or not testpid(pid))
1316 1316 and not condfn()):
1317 1317 return -1
1318 1318 time.sleep(0.1)
1319 1319 return pid
1320 1320 finally:
1321 1321 if prevhandler is not None:
1322 1322 signal.signal(signal.SIGCHLD, prevhandler)
1323 1323
1324 1324 try:
1325 1325 any, all = any, all
1326 1326 except NameError:
1327 1327 def any(iterable):
1328 1328 for i in iterable:
1329 1329 if i:
1330 1330 return True
1331 1331 return False
1332 1332
1333 1333 def all(iterable):
1334 1334 for i in iterable:
1335 1335 if not i:
1336 1336 return False
1337 1337 return True
1338 1338
1339 1339 def termwidth():
1340 1340 if 'COLUMNS' in os.environ:
1341 1341 try:
1342 1342 return int(os.environ['COLUMNS'])
1343 1343 except ValueError:
1344 1344 pass
1345 1345 return termwidth_()
General Comments 0
You need to be logged in to leave comments. Login now