Package conary :: Package lib :: Module util
[hide private]
[frames] | no frames]

Source Code for Module conary.lib.util

   1  # 
   2  # Copyright (c) 2004-2008 rPath, Inc. 
   3  # 
   4  # This program is distributed under the terms of the Common Public License, 
   5  # version 1.0. A copy of this license should have been distributed with this 
   6  # source file in a file called LICENSE. If it is not present, the license 
   7  # is always available at http://www.rpath.com/permanent/licenses/CPL-1.0. 
   8  # 
   9  # This program is distributed in the hope that it will be useful, but 
  10  # without any warranty; without even the implied warranty of merchantability 
  11  # or fitness for a particular purpose. See the Common Public License for 
  12  # full details. 
  13  # 
  14   
  15  import bdb 
  16  import bz2 
  17  import debugger 
  18  import fcntl 
  19  import errno 
  20  import itertools 
  21  import log 
  22  import misc 
  23  import os 
  24  import re 
  25  import select 
  26  import shutil 
  27  import signal 
  28  import stat 
  29  import string 
  30  import StringIO 
  31  import subprocess 
  32  import struct 
  33  import sys 
  34  import tempfile 
  35  import time 
  36  import types 
  37  import urllib 
  38  import urlparse 
  39  import weakref 
  40  import xmlrpclib 
  41  import zlib 
  42   
  43  from conary.lib import fixedglob, graph, log, api 
  44   
  45  # Imported for the benefit of older code, 
  46  from conary.lib.formattrace import formatTrace 
  47   
  48   
  49  # Simple ease-of-use extensions to python libraries 
  50   
51 -def normpath(path):
52 s = os.path.normpath(path) 53 if s.startswith(os.sep + os.sep): 54 return s[1:] 55 return s
56
57 -def realpath(path):
58 # returns the real path of a file, if and only if it is not a symbolic 59 # link 60 if not os.path.exists(path): 61 return path 62 if stat.S_ISLNK(os.lstat(path)[stat.ST_MODE]): 63 return path 64 return os.path.realpath(path)
65
66 -def isregular(path):
67 return stat.S_ISREG(os.lstat(path)[stat.ST_MODE])
68 69
70 -def _mkdirs(path, mode=0777):
71 """ 72 Recursive helper to L{mkdirChain}. Internal use only. 73 """ 74 head, tail = os.path.split(path) 75 if head and tail and not os.path.exists(head): 76 _mkdirs(head, mode) 77 78 # Make the directory while ignoring errors about it existing. 79 misc.mkdirIfMissing(path)
80 81 82 @api.developerApi
83 -def mkdirChain(*paths):
84 """ 85 Make one or more directories if they do not already exist, including any 86 needed parent directories. Similar to L{os.makedirs} except that it does 87 not error if the requested directory already exists, and it is more 88 resilient to race conditions. 89 """ 90 for path in paths: 91 path = normpath(os.path.abspath(path)) 92 if not os.path.exists(path): 93 _mkdirs(path)
94 95
96 -def _searchVisit(arg, dirname, names):
97 file = arg[0] 98 path = arg[1] 99 testname = '%s%s%s' %(dirname, os.sep, file) 100 if file in names: 101 path[0] = testname 102 del names
103
104 -def searchPath(file, basepath):
105 path = [ None ] 106 # XXX replace with os.walk in python 2.3, to cut short properly 107 os.path.walk(basepath, _searchVisit, (file, path)) 108 return path[0]
109
110 -def searchFile(file, searchdirs, error=None):
111 for dir in searchdirs: 112 s = "%s%s%s" %(dir, os.sep, file) 113 if os.path.exists(s): 114 return s 115 if error: 116 raise OSError, (errno.ENOENT, os.strerror(errno.ENOENT)) 117 return None
118
119 -def findFile(file, searchdirs):
120 return searchFile(file, searchdirs, error=1)
121
122 -def which (filename):
123 if not os.environ.has_key('PATH') or os.environ['PATH'] == '': 124 p = os.defpath 125 else: 126 p = os.environ['PATH'] 127 128 pathlist = p.split (os.pathsep) 129 130 for path in pathlist: 131 f = os.path.join(path, filename) 132 if os.access(f, os.X_OK): 133 return f 134 return None
135
136 -def recurseDirectoryList(topdir, withDirs=False):
137 """Recursively list all files in the directory""" 138 items = [topdir] 139 while items: 140 item = items.pop() 141 if os.path.islink(item) or os.path.isfile(item): 142 yield item 143 continue 144 # Directory 145 listdir = os.listdir(item) 146 # Add the contents of the directory in reverse order (we use pop(), so 147 # last element in the list is the one popped out) 148 listdir.sort() 149 listdir.reverse() 150 listdir = [ os.path.join(item, x) for x in listdir ] 151 items.extend(listdir) 152 153 if withDirs: 154 # This is useful if one wants to catch empty directories 155 yield item
156
157 -def normurl(url):
158 surl = list(urlparse.urlsplit(url)) 159 if surl[2] == '': 160 surl[2] = '/' 161 elif surl[2] != '/': 162 tail = '' 163 if surl[2].endswith('/'): 164 tail = '/' 165 surl[2] = normpath(surl[2]) + tail 166 return urlparse.urlunsplit(surl)
167 168 errorMessage = ''' 169 ERROR: An unexpected condition has occurred in Conary. This is 170 most likely due to insufficient handling of erroneous input, but 171 may be some other bug. In either case, please report the error at 172 http://issues.rpath.com/ and attach to the issue the file 173 %(stackfile)s 174 175 Then, for more complete information, please run the following script: 176 conary-debug "%(command)s" 177 You can attach the resulting archive to your issue report at 178 http://issues.rpath.com/ For more information, or if you have 179 trouble with the conary-debug command, go to: 180 http://wiki.rpath.com/wiki/Conary:How_To_File_An_Effective_Bug_Report 181 182 To get a debug prompt, rerun the command with --debug-all 183 184 Error details follow: 185 186 %(filename)s:%(lineno)s 187 %(errtype)s: %(errmsg)s 188 189 The complete related traceback has been saved as %(stackfile)s 190 ''' 191 _debugAll = False 192 193 @api.developerApi
194 -def genExcepthook(debug=True, 195 debugCtrlC=False, prefix='conary-error-', 196 catchSIGUSR1=True, error=errorMessage):
197 def SIGUSR1Handler(signum, frame): 198 global _debugAll 199 _debugAll = True 200 print >>sys.stderr, '<Turning on KeyboardInterrupt catching>'
201 202 def excepthook(typ, value, tb): 203 if typ is bdb.BdbQuit: 204 sys.exit(1) 205 #pylint: disable-msg=E1101 206 sys.excepthook = sys.__excepthook__ 207 if not _debugAll and (typ == KeyboardInterrupt and not debugCtrlC): 208 sys.exit(1) 209 210 out = BoundedStringIO() 211 formatTrace(typ, value, tb, stream = out, withLocals = False) 212 out.write("\nFull stack:\n") 213 formatTrace(typ, value, tb, stream = out, withLocals = True) 214 out.seek(0) 215 tbString = out.read() 216 del out 217 if log.syslog is not None: 218 log.syslog("command failed\n%s", tbString) 219 220 if debug or _debugAll: 221 formatTrace(typ, value, tb, stream = sys.stderr, 222 withLocals = False) 223 if sys.stdout.isatty() and sys.stdin.isatty(): 224 debugger.post_mortem(tb, typ, value) 225 else: 226 sys.exit(1) 227 elif log.getVerbosity() is log.DEBUG: 228 log.debug(tbString) 229 else: 230 cmd = sys.argv[0] 231 if cmd.endswith('/commands/conary'): 232 cmd = cmd[:len('/commands/conary')] + '/bin/conary' 233 elif cmd.endswith('/commands/cvc'): 234 cmd = cmd[:len('/commands/cvc')] + '/bin/cvc' 235 236 origTb = tb 237 cmd = normpath(cmd) 238 sys.argv[0] = cmd 239 while tb.tb_next: tb = tb.tb_next 240 lineno = tb.tb_frame.f_lineno 241 filename = tb.tb_frame.f_code.co_filename 242 tmpfd, stackfile = tempfile.mkstemp('.txt', prefix) 243 os.write(tmpfd, tbString) 244 os.close(tmpfd) 245 246 sys.stderr.write(error % dict(command=' '.join(sys.argv), 247 filename=filename, 248 lineno=lineno, 249 errtype=typ.__name__, 250 errmsg=value, 251 stackfile=stackfile)) 252 253 #if catchSIGUSR1: 254 # signal.signal(signal.SIGUSR1, SIGUSR1Handler) 255 return excepthook 256 257 258
259 -def _handle_rc(rc, cmd):
260 if rc: 261 if not os.WIFEXITED(rc): 262 info = 'Shell command "%s" killed with signal %d' \ 263 %(cmd, os.WTERMSIG(rc)) 264 if os.WEXITSTATUS(rc): 265 info = 'Shell command "%s" exited with exit code %d' \ 266 %(cmd, os.WEXITSTATUS(rc)) 267 log.error(info) 268 raise RuntimeError, info
269
270 -def execute(cmd, destDir=None, verbose=True):
271 """ 272 similar to os.system, but raises errors if exit code != 0 and closes stdin 273 so processes can never block on user input 274 """ 275 if verbose: 276 log.info(cmd) 277 rc = subprocess.call(cmd, shell=True, cwd=destDir, stdin=open(os.devnull)) 278 # form the rc into a standard exit status 279 if rc < 0: 280 # turn rc positive 281 rc = rc * -1 282 else: 283 # shift the return code into the high bits 284 rc = rc << 8 285 _handle_rc(rc, cmd)
286
287 -class popen:
288 """ 289 Version of popen() that throws errors on close(), unlike os.popen() 290 """ 291 # unfortunately, can't derive from os.popen. Add methods as necessary.
292 - def __init__(self, *args):
293 self.p = os.popen(*args) 294 self.write = self.p.write 295 self.read = self.p.read 296 self.readline = self.p.readline 297 self.readlines = self.p.readlines 298 self.writelines = self.p.writelines
299
300 - def close(self, *args):
301 rc = self.p.close(*args) 302 _handle_rc(rc, self.p.name) 303 return rc
304 305 # string extensions 306
307 -def find(s, subs, start=0):
308 ret = -1 309 found = None 310 for sub in subs: 311 this = string.find(s, sub, start) 312 if this > -1 and ( ret < 0 or this < ret): 313 ret = this 314 found = s[this:this+1] 315 return (ret, found)
316
317 -def literalRegex(s):
318 return re.escape(s)
319 320 321 # shutil module extensions, with {}-expansion and globbing
322 -class BraceExpander(object):
323 """Class encapsulating the logic required by the brace expander parser"""
324 - class Alternative(list):
325 - def __repr__(self):
326 return "Alternative%s" % list.__repr__(self)
327 - class Product(list):
328 - def __repr__(self):
329 return "Product%s" % list.__repr__(self)
330 - class Comma(object):
331 "Comma operator"
332 - class Concat(object):
333 "Concatenation operator"
334 335 @classmethod
336 - def _collapseNode(cls, node):
337 if isinstance(node, basestring): 338 # Char data 339 return [ node ] 340 if not node: 341 return [] 342 components = [ cls._collapseNode(x) for x in node ] 343 if isinstance(node, cls.Product): 344 ret = cls._cartesianProduct(components) 345 return ret 346 ret = [] 347 for comp in components: 348 ret.extend(comp) 349 if not isinstance(node, cls.Alternative) or len(components) != 1: 350 return ret 351 # CNY-3158 - single-length items should not be expanded 352 return [ '{%s}' % x for x in ret ]
353 354 @classmethod
355 - def _cartesianProduct(cls, components):
356 ret = list(components.pop()) 357 while components: 358 comp = components.pop() 359 nret = [] 360 for j in comp: 361 nret.extend("%s%s" % (j, x) for x in ret) 362 ret = nret 363 return ret
364 365 @classmethod
366 - def _reversePolishNotation(cls, listObj):
367 haveComma = False 368 haveText = False 369 # Sentinel 370 listObj.append(None) 371 outputQ = [] 372 operators = [] 373 lastWasLiteral = False 374 for item in listObj: 375 if isinstance(item, basestring): 376 if not haveText: 377 text = [] 378 outputQ.append(text) 379 haveText = True 380 else: 381 text = outputQ[-1] 382 text.append(item) 383 continue 384 if haveText: 385 topNode = outputQ.pop() 386 topNode = ''.join(topNode) 387 haveText = False 388 outputQ.append(topNode) 389 lastWasLiteral = True 390 391 if item is None: 392 # We've reached the sentinel 393 break 394 if item is cls.Comma: 395 haveComma = True 396 lastWasLiteral = False 397 while operators: 398 op = operators.pop() 399 outputQ.append(op) 400 operators.append(item) 401 continue 402 outputQ.append(item) 403 if not lastWasLiteral: 404 lastWasLiteral = True 405 continue 406 # Concatenation 407 while operators and operators[-1] is not cls.Comma: 408 op = operators.pop() 409 outputQ.append(op) 410 operators.append(cls.Concat) 411 while operators: 412 op = operators.pop() 413 outputQ.append(op) 414 # Now collapse into meaningful nodes 415 stack = [] 416 opMap = { 417 cls.Comma: cls.Alternative, 418 cls.Concat: cls.Product, 419 } 420 for item in outputQ: 421 if not (item is cls.Comma or item is cls.Concat): 422 stack.append(item) 423 continue 424 op2 = stack.pop() 425 op1 = stack.pop() 426 ncls = opMap[item] 427 if isinstance(op1, ncls): 428 op1.append(op2) 429 stack.append(op1) 430 elif isinstance(op2, ncls): 431 op2[0:0] = [op1] 432 stack.append(op2) 433 else: 434 nobj = ncls() 435 nobj.extend([op1, op2]) 436 stack.append(nobj) 437 ret = stack[0] 438 if not haveComma: 439 ret = cls.Alternative([ret]) 440 return ret
441 442 @classmethod
443 - def removeComma(cls, l):
444 for item in l: 445 if item is cls.Comma: 446 yield ',' 447 else: 448 yield item
449 450 @classmethod
451 - def braceExpand(cls, path):
452 stack = [ cls.Product() ] 453 isEscaping = False 454 for c in path: 455 if isEscaping: 456 isEscaping = False 457 stack[-1].append(c) 458 continue 459 if c == '\\': 460 isEscaping = True 461 continue 462 if c == '{': 463 stack.append([]) 464 continue 465 if not stack: 466 raise ValueError, 'path %s has unbalanced {}' %path 467 if c == '}': 468 if len(stack) == 1: 469 # Unbalanced }; add it as literal 470 stack[-1].append(c) 471 continue 472 n = stack.pop() 473 # ,} case 474 if n and n[-1] is cls.Comma: 475 n.append("") 476 stack[-1].append(cls._reversePolishNotation(n)) 477 continue 478 if c == ',': 479 # Mark the comma separator, but only if a previous { was 480 # found, otherwise treat it as a regular character 481 if len(stack) > 1: 482 # {,a} case - leading comma will produce an empty string 483 if not stack[-1]: 484 stack[-1].append("") 485 c = cls.Comma 486 stack[-1].append(c) 487 if len(stack) > 1: 488 # Unbalanced {; add it as literal 489 node = stack[0] 490 for onode in stack[1:]: 491 node.append('{') 492 node.extend(cls.removeComma(onode)) 493 node = stack[0] 494 del stack 495 # We need to filter empty strings from the output: 496 # a{,b} should produce a ab while {,a} should produce a 497 return [ x for x in cls._collapseNode(node) if x]
498
499 -def braceExpand(path):
500 return BraceExpander.braceExpand(path)
501 502 @api.publicApi
503 -def braceGlob(paths):
504 """ 505 @raises ValueError: raised if paths has unbalanced braces 506 @raises OSError: raised in some cases where lstat on a path fails 507 """ 508 pathlist = [] 509 for path in braceExpand(paths): 510 pathlist.extend(fixedglob.glob(path)) 511 return pathlist
512 513 @api.developerApi
514 -def rmtree(paths, ignore_errors=False, onerror=None):
515 for path in braceGlob(paths): 516 log.debug('deleting [tree] %s', path) 517 # act more like rm -rf -- allow files, too 518 if (os.path.islink(path) or 519 (os.path.exists(path) and not os.path.isdir(path))): 520 os.remove(path) 521 else: 522 os.path.walk(path, _permsVisit, None) 523 shutil.rmtree(path, ignore_errors, onerror)
524
525 -def _permsVisit(arg, dirname, names):
526 for name in names: 527 path = dirname + os.sep + name 528 mode = os.lstat(path)[stat.ST_MODE] 529 # has to be executable to cd, readable to list, writeable to delete 530 if stat.S_ISDIR(mode) and (mode & 0700) != 0700: 531 log.warning("working around illegal mode 0%o at %s", mode, path) 532 mode |= 0700 533 os.chmod(path, mode)
534
535 -def remove(paths, quiet=False):
536 for path in braceGlob(paths): 537 if os.path.isdir(path) and not os.path.islink(path): 538 log.warning('Not removing directory %s', path) 539 elif os.path.exists(path) or os.path.islink(path): 540 if not quiet: 541 log.debug('deleting [file] %s', path) 542 os.remove(path) 543 else: 544 log.warning('file %s does not exist when attempting to delete [file]', path)
545
546 -def copyfile(sources, dest, verbose=True):
547 for source in braceGlob(sources): 548 if verbose: 549 log.info('copying %s to %s', source, dest) 550 shutil.copy2(source, dest)
551
552 -def copyfileobj(source, dest, callback = None, digest = None, 553 abortCheck = None, bufSize = 128*1024, rateLimit = None, 554 sizeLimit = None, total=0):
555 if hasattr(dest, 'send'): 556 write = dest.send 557 else: 558 write = dest.write 559 560 if rateLimit is None: 561 rateLimit = 0 562 563 if not rateLimit == 0: 564 if rateLimit < 8 * 1024: 565 bufSize = 4 * 1024 566 else: 567 bufSize = 8 * 1024 568 569 rateLimit = float(rateLimit) 570 571 starttime = time.time() 572 573 copied = 0 574 575 if abortCheck: 576 pollObj = select.poll() 577 pollObj.register(source.fileno(), select.POLLIN) 578 else: 579 pollObj = None 580 581 while True: 582 if sizeLimit and (sizeLimit - copied < bufSize): 583 bufSize = sizeLimit - copied 584 585 if abortCheck: 586 # if we need to abortCheck, make sure we check it every time 587 # read returns, and every five seconds 588 l = [] 589 while not l: 590 if abortCheck(): 591 return None 592 l = pollObj.poll(5000) 593 594 buf = source.read(bufSize) 595 if not buf: 596 break 597 598 total += len(buf) 599 copied += len(buf) 600 write(buf) 601 602 if digest: 603 digest.update(buf) 604 605 now = time.time() 606 if now == starttime: 607 rate = 0 # don't bother limiting download until now > starttime. 608 else: 609 rate = copied / ((now - starttime)) 610 611 if callback: 612 callback(total, rate) 613 614 if copied == sizeLimit: 615 break 616 617 if rateLimit > 0 and rate > rateLimit: 618 time.sleep((copied / rateLimit) - (copied / rate)) 619 620 return copied
621
622 -def rename(sources, dest):
623 for source in braceGlob(sources): 624 log.debug('renaming %s to %s', source, dest) 625 os.rename(source, dest)
626
627 -def _copyVisit(arg, dirname, names):
628 sourcelist = arg[0] 629 sourcelen = arg[1] 630 dest = arg[2] 631 filemode = arg[3] 632 dirmode = arg[4] 633 if dirmode: 634 os.chmod(dirname, dirmode) 635 for name in names: 636 if filemode: 637 os.chmod(dirname+os.sep+name, filemode) 638 sourcelist.append(os.path.normpath( 639 dest + os.sep + dirname[sourcelen:] + os.sep + name))
640
641 -def copytree(sources, dest, symlinks=False, filemode=None, dirmode=None):
642 """ 643 Copies tree(s) from sources to dest, returning a list of 644 the filenames that it has written. 645 """ 646 sourcelist = [] 647 for source in braceGlob(sources): 648 if os.path.isdir(source): 649 if source[-1] == '/': 650 source = source[:-1] 651 thisdest = '%s%s%s' %(dest, os.sep, os.path.basename(source)) 652 log.debug('copying [tree] %s to %s', source, thisdest) 653 shutil.copytree(source, thisdest, symlinks) 654 if dirmode: 655 os.chmod(thisdest, dirmode) 656 os.path.walk(source, _copyVisit, 657 (sourcelist, len(source), thisdest, filemode, dirmode)) 658 else: 659 log.debug('copying [file] %s to %s', source, dest) 660 shutil.copy2(source, dest) 661 if dest.endswith(os.sep): 662 thisdest = dest + os.sep + os.path.basename(source) 663 else: 664 thisdest = dest 665 if filemode: 666 os.chmod(thisdest, filemode) 667 sourcelist.append(thisdest) 668 return sourcelist
669
670 -def checkPath(binary, root=None):
671 """ 672 Examine $PATH to determine if a binary exists, returns full pathname 673 if it exists; otherwise None. 674 """ 675 path = os.environ.get('PATH', '') 676 if binary[0] == '/': 677 # handle case where binary starts with / seperately 678 # because os.path.join will not do the right 679 # thing with root set. 680 if root: 681 if os.path.exists(root + binary): 682 return root + binary 683 elif os.path.exists(binary): 684 return binary 685 return None 686 687 for path in path.split(os.pathsep): 688 if root: 689 path = joinPaths(root, path) 690 candidate = os.path.join(path, binary) 691 if os.access(candidate, os.X_OK): 692 if root: 693 return candidate[len(root):] 694 return candidate 695 return None
696
697 -def joinPaths(*args):
698 return normpath(os.sep.join(args))
699
700 -def splitPathReverse(path):
701 """Split the path at the operating system's separators. 702 Returns a list with the path components in reverse order. 703 Empty path components are stripped out. 704 Example: 'a//b//c/d' -> ['d', 'c', 'b', 'a'] 705 """ 706 while 1: 707 path, tail = os.path.split(path) 708 if not tail: 709 break 710 yield tail
711
712 -def splitPath(path):
713 """Split the path at the operating system's separators 714 Empty path components are stripped out 715 Example: 'a//b//c/d' -> ['a', 'b', 'c', 'd'] 716 """ 717 ret = list(splitPathReverse(path)) 718 ret.reverse() 719 return ret
720
721 -def assertIteratorAtEnd(iter):
722 try: 723 iter.next() 724 raise AssertionError 725 except StopIteration: 726 return True
727 728 ref = weakref.ref
729 -class ObjectCache(dict):
730 """ 731 Implements a cache of arbitrary (hashable) objects where an object 732 can be looked up and have its cached value retrieved. This allows 733 a single copy of immutable objects to be kept in memory. 734 """
735 - def __init__(self, *args):
736 dict.__init__(self, *args) 737 738 def remove(k, selfref=ref(self)): 739 self = selfref() 740 if self is not None: 741 return dict.__delitem__(self, k)
742 self._remove = remove
743
744 - def __setitem__(self, key, value):
745 return dict.__setitem__(self, ref(key, self._remove), ref(value))
746
747 - def __contains__(self, key):
748 return dict.__contains__(self, ref(key))
749
750 - def has_key(self, key):
751 return key in self
752
753 - def __delitem__(self, key):
754 return dict.__delitem__(self, ref(key))
755
756 - def __getitem__(self, key):
757 return dict.__getitem__(self, ref(key))()
758
759 - def setdefault(self, key, value):
760 return dict.setdefault(self, ref(key, self._remove), ref(value))()
761
762 -def memsize(pid = None):
763 return memusage(pid = pid)[0]
764
765 -def memusage(pid = None):
766 """Get the memory usage. 767 @param pid: Process to analyze (None for current process) 768 """ 769 if pid is None: 770 pfn = "/proc/self/statm" 771 else: 772 pfn = "/proc/%d/statm" % pid 773 line = open(pfn).readline() 774 # Assume page size is 4k (true for i386). This can be adjusted by reading 775 # resource.getpagesize() 776 arr = [ 4 * int(x) for x in line.split()[:6] ] 777 vmsize, vmrss, vmshared, text, lib, data = arr 778 779 # The RHS in the following description is the fields in /proc/self/status 780 # text is VmExe 781 # data is VmData + VmStk 782 return vmsize, vmrss, vmshared, text, lib, data
783 793
794 -def tupleListBsearchInsert(haystack, newItem, cmpFn):
795 """ 796 Inserts newItem into haystack, maintaining the sorted order. The 797 cmpIdx is the item number in the list of tuples to base comparisons on. 798 Duplicates items aren't added. Returns True if the item was added, 799 False if it was already present. 800 801 @param haystack: list of tuples. 802 @type haystack: list 803 @param newItem: The item to be inserted 804 @type newItem: tuple 805 @param cmpFn: Comparison function 806 @type cmpFn: function 807 @rtype: bool 808 """ 809 start = 0 810 finish = len(haystack) - 1 811 while start < finish: 812 i = (start + finish) / 2 813 814 rc = cmpFn(haystack[i], newItem) 815 if rc == 0: 816 start = i 817 finish = i 818 break 819 elif rc < 0: 820 start = i + 1 821 else: 822 finish = i - 1 823 824 if start >= len(haystack): 825 haystack.append(newItem) 826 else: 827 rc = cmpFn(haystack[start], newItem) 828 if rc < 0: 829 haystack.insert(start + 1, newItem) 830 elif rc > 0: 831 haystack.insert(start, newItem) 832 else: 833 return False 834 835 return True
836 837 _tempdir = tempfile.gettempdir()
838 -def settempdir(tempdir):
839 # XXX add locking if we ever go multi-threadded 840 global _tempdir 841 _tempdir = tempdir
842
843 -def mkstemp(suffix="", prefix=tempfile.template, dir=None, text=False):
844 """ 845 a wrapper for tempfile.mkstemp that uses a common prefix which 846 is set through settempdir() 847 """ 848 if dir is None: 849 global _tempdir 850 dir = _tempdir 851 return tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir, text=text)
852
853 -class SendableFileSet:
854 855 tags = {} 856 ptrSize = len(struct.pack("@P", 0)) 857 858 @staticmethod
859 - def _register(klass):
860 SendableFileSet.tags[klass._tag] = klass
861
862 - def __init__(self):
863 self.l = []
864 865 @staticmethod
866 - def sendObjIds(sock, l):
867 sendmsg(sock, [ struct.