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