Package SCons :: Module Util
[hide private]
[frames] | no frames]

Source Code for Module SCons.Util

   1  """SCons.Util 
   2   
   3  Various utility functions go here. 
   4   
   5  """ 
   6   
   7  # 
   8  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
   9  # 
  10  # Permission is hereby granted, free of charge, to any person obtaining 
  11  # a copy of this software and associated documentation files (the 
  12  # "Software"), to deal in the Software without restriction, including 
  13  # without limitation the rights to use, copy, modify, merge, publish, 
  14  # distribute, sublicense, and/or sell copies of the Software, and to 
  15  # permit persons to whom the Software is furnished to do so, subject to 
  16  # the following conditions: 
  17  # 
  18  # The above copyright notice and this permission notice shall be included 
  19  # in all copies or substantial portions of the Software. 
  20  # 
  21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  22  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  23  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  24  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  25  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  26  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  27  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  28  # 
  29   
  30  __revision__ = "src/engine/SCons/Util.py 2725 2008/03/31 12:52:02 knight" 
  31   
  32  import SCons.compat 
  33   
  34  import copy 
  35  import os 
  36  import os.path 
  37  import re 
  38  import string 
  39  import sys 
  40  import types 
  41   
  42  from UserDict import UserDict 
  43  from UserList import UserList 
  44  from UserString import UserString 
  45   
  46  # Don't "from types import ..." these because we need to get at the 
  47  # types module later to look for UnicodeType. 
  48  DictType        = types.DictType 
  49  InstanceType    = types.InstanceType 
  50  ListType        = types.ListType 
  51  StringType      = types.StringType 
  52  TupleType       = types.TupleType 
  53   
54 -def dictify(keys, values, result={}):
55 for k, v in zip(keys, values): 56 result[k] = v 57 return result
58 59 _altsep = os.altsep 60 if _altsep is None and sys.platform == 'win32': 61 # My ActivePython 2.0.1 doesn't set os.altsep! What gives? 62 _altsep = '/' 63 if _altsep:
64 - def rightmost_separator(path, sep, _altsep=_altsep):
65 rfind = string.rfind 66 return max(rfind(path, sep), rfind(path, _altsep))
67 else: 68 rightmost_separator = string.rfind 69 70 # First two from the Python Cookbook, just for completeness. 71 # (Yeah, yeah, YAGNI...)
72 -def containsAny(str, set):
73 """Check whether sequence str contains ANY of the items in set.""" 74 for c in set: 75 if c in str: return 1 76 return 0
77
78 -def containsAll(str, set):
79 """Check whether sequence str contains ALL of the items in set.""" 80 for c in set: 81 if c not in str: return 0 82 return 1
83
84 -def containsOnly(str, set):
85 """Check whether sequence str contains ONLY items in set.""" 86 for c in str: 87 if c not in set: return 0 88 return 1
89
90 -def splitext(path):
91 "Same as os.path.splitext() but faster." 92 sep = rightmost_separator(path, os.sep) 93 dot = string.rfind(path, '.') 94 # An ext is only real if it has at least one non-digit char 95 if dot > sep and not containsOnly(path[dot:], "0123456789."): 96 return path[:dot],path[dot:] 97 else: 98 return path,""
99
100 -def updrive(path):
101 """ 102 Make the drive letter (if any) upper case. 103 This is useful because Windows is inconsitent on the case 104 of the drive letter, which can cause inconsistencies when 105 calculating command signatures. 106 """ 107 drive, rest = os.path.splitdrive(path) 108 if drive: 109 path = string.upper(drive) + rest 110 return path
111 112 # 113 # Generic convert-to-string functions that abstract away whether or 114 # not the Python we're executing has Unicode support. The wrapper 115 # to_String_for_signature() will use a for_signature() method if the 116 # specified object has one. 117 # 118 if hasattr(types, 'UnicodeType'): 119 UnicodeType = types.UnicodeType
120 - def to_String(s):
121 if isinstance(s, UserString): 122 t = type(s.data) 123 else: 124 t = type(s) 125 if t is UnicodeType: 126 return unicode(s) 127 else: 128 return str(s)
129 else: 130 to_String = str 131
132 -def to_String_for_signature(obj):
133 try: 134 f = obj.for_signature 135 except AttributeError: 136 return to_String_for_subst(obj) 137 else: 138 return f()
139
140 -def to_String_for_subst(s):
141 if is_Sequence( s ): 142 return string.join( map(to_String_for_subst, s) ) 143 144 return to_String( s )
145 146
147 -class CallableComposite(UserList):
148 """A simple composite callable class that, when called, will invoke all 149 of its contained callables with the same arguments."""
150 - def __call__(self, *args, **kwargs):
151 retvals = map(lambda x, args=args, kwargs=kwargs: apply(x, 152 args, 153 kwargs), 154 self.data) 155 if self.data and (len(self.data) == len(filter(callable, retvals))): 156 return self.__class__(retvals) 157 return NodeList(retvals)
158
159 -class NodeList(UserList):
160 """This class is almost exactly like a regular list of Nodes 161 (actually it can hold any object), with one important difference. 162 If you try to get an attribute from this list, it will return that 163 attribute from every item in the list. For example: 164 165 >>> someList = NodeList([ ' foo ', ' bar ' ]) 166 >>> someList.strip() 167 [ 'foo', 'bar' ] 168 """
169 - def __nonzero__(self):
170 return len(self.data) != 0
171
172 - def __str__(self):
173 return string.join(map(str, self.data))
174
175 - def __getattr__(self, name):
176 if not self.data: 177 # If there is nothing in the list, then we have no attributes to 178 # pass through, so raise AttributeError for everything. 179 raise AttributeError, "NodeList has no attribute: %s" % name 180 181 # Return a list of the attribute, gotten from every element 182 # in the list 183 attrList = map(lambda x, n=name: getattr(x, n), self.data) 184 185 # Special case. If the attribute is callable, we do not want 186 # to return a list of callables. Rather, we want to return a 187 # single callable that, when called, will invoke the function on 188 # all elements of this list. 189 if self.data and (len(self.data) == len(filter(callable, attrList))): 190 return CallableComposite(attrList) 191 return self.__class__(attrList)
192 193 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') 194
195 -def get_environment_var(varstr):
196 """Given a string, first determine if it looks like a reference 197 to a single environment variable, like "$FOO" or "${FOO}". 198 If so, return that variable with no decorations ("FOO"). 199 If not, return None.""" 200 mo=_get_env_var.match(to_String(varstr)) 201 if mo: 202 var = mo.group(1) 203 if var[0] == '{': 204 return var[1:-1] 205 else: 206 return var 207 else: 208 return None
209
210 -class DisplayEngine:
211 - def __init__(self):
212 self.__call__ = self.print_it
213
214 - def print_it(self, text, append_newline=1):
215 if append_newline: text = text + '\n' 216 sys.stdout.write(text)
217
218 - def dont_print(self, text, append_newline=1):
219 pass
220
221 - def set_mode(self, mode):
222 if mode: 223 self.__call__ = self.print_it 224 else: 225 self.__call__ = self.dont_print
226
227 -def render_tree(root, child_func, prune=0, margin=[0], visited={}):
228 """ 229 Render a tree of nodes into an ASCII tree view. 230 root - the root node of the tree 231 child_func - the function called to get the children of a node 232 prune - don't visit the same node twice 233 margin - the format of the left margin to use for children of root. 234 1 results in a pipe, and 0 results in no pipe. 235 visited - a dictionary of visited nodes in the current branch if not prune, 236 or in the whole tree if prune. 237 """ 238 239 rname = str(root) 240 241 children = child_func(root) 242 retval = "" 243 for pipe in margin[:-1]: 244 if pipe: 245 retval = retval + "| " 246 else: 247 retval = retval + " " 248 249 if visited.has_key(rname): 250 return retval + "+-[" + rname + "]\n" 251 252 retval = retval + "+-" + rname + "\n" 253 if not prune: 254 visited = copy.copy(visited) 255 visited[rname] = 1 256 257 for i in range(len(children)): 258 margin.append(i<len(children)-1) 259 retval = retval + render_tree(children[i], child_func, prune, margin, visited 260 ) 261 margin.pop() 262 263 return retval
264 265 IDX = lambda N: N and 1 or 0 266 318 margins = map(MMM, margin[:-1]) 319 320 children = child_func(root) 321 322 if prune and visited.has_key(rname) and children: 323 print string.join(tags + margins + ['+-[', rname, ']'], '') 324 return 325 326 print string.join(tags + margins + ['+-', rname], '') 327 328 visited[rname] = 1 329 330 if children: 331 margin.append(1) 332 map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited: 333 print_tree(C, cf, p, i, m, v), 334 children[:-1]) 335 margin[-1] = 0 336 print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited) 337 margin.pop() 338 339 340 341 # Functions for deciding if things are like various types, mainly to 342 # handle UserDict, UserList and UserString like their underlying types. 343 # 344 # Yes, all of this manual testing breaks polymorphism, and the real 345 # Pythonic way to do all of this would be to just try it and handle the 346 # exception, but handling the exception when it's not the right type is 347 # often too slow. 348 349 try:
350 - class mystr(str):
351 pass
352 except TypeError: 353 # An older Python version without new-style classes. 354 # 355 # The actual implementations here have been selected after timings 356 # coded up in in bench/is_types.py (from the SCons source tree, 357 # see the scons-src distribution), mostly against Python 1.5.2. 358 # Key results from those timings: 359 # 360 # -- Storing the type of the object in a variable (t = type(obj)) 361 # slows down the case where it's a native type and the first 362 # comparison will match, but nicely speeds up the case where 363 # it's a different native type. Since that's going to be 364 # common, it's a good tradeoff. 365 # 366 # -- The data show that calling isinstance() on an object that's 367 # a native type (dict, list or string) is expensive enough 368 # that checking up front for whether the object is of type 369 # InstanceType is a pretty big win, even though it does slow 370 # down the case where it really *is* an object instance a 371 # little bit.
372 - def is_Dict(obj):
373 t = type(obj) 374 return t is DictType or \ 375 (t is InstanceType and isinstance(obj, UserDict))
376
377 - def is_List(obj):
378 t = type(obj) 379 return t is ListType \ 380 or (t is InstanceType and isinstance(obj, UserList))
381
382 - def is_Sequence(obj):
383 t = type(obj) 384 return t is ListType \ 385 or t is TupleType \ 386 or (t is InstanceType and isinstance(obj, UserList))
387
388 - def is_Tuple(obj):
389 t = type(obj) 390 return t is TupleType
391 392 if hasattr(types, 'UnicodeType'):
393 - def is_String(obj):
394 t = type(obj) 395 return t is StringType \ 396 or t is UnicodeType \ 397 or (t is InstanceType and isinstance(obj, UserString))
398 else:
399 - def is_String(obj):
400 t = type(obj) 401 return t is StringType \ 402 or (t is InstanceType and isinstance(obj, UserString))
403
404 - def is_Scalar(obj):
405 return is_String(obj) or not is_Sequence(obj)
406
407 - def flatten(obj, result=None):
408 """Flatten a sequence to a non-nested list. 409 410 Flatten() converts either a single scalar or a nested sequence 411 to a non-nested list. Note that flatten() considers strings 412 to be scalars instead of sequences like Python would. 413 """ 414 if is_Scalar(obj): 415 return [obj] 416 if result is None: 417 result = [] 418 for item in obj: 419 if is_Scalar(item): 420 result.append(item) 421 else: 422 flatten_sequence(item, result) 423 return result
424
425 - def flatten_sequence(sequence, result=None):
426 """Flatten a sequence to a non-nested list. 427 428 Same as flatten(), but it does not handle the single scalar 429 case. This is slightly more efficient when one knows that 430 the sequence to flatten can not be a scalar. 431 """ 432 if result is None: 433 result = [] 434 for item in sequence: 435 if is_Scalar(item): 436 result.append(item) 437 else: 438 flatten_sequence(item, result) 439 return result
440 else: 441 # A modern Python version with new-style classes, so we can just use 442 # isinstance(). 443 # 444 # We are using the following trick to speed-up these 445 # functions. Default arguments are used to take a snapshot of the 446 # the global functions and constants used by these functions. This 447 # transforms accesses to global variable into local variables 448 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). 449 450 DictTypes = (dict, UserDict) 451 ListTypes = (list, UserList) 452 SequenceTypes = (list, tuple, UserList) 453 454 # Empirically, Python versions with new-style classes all have 455 # unicode. 456 # 457 # Note that profiling data shows a speed-up when comparing 458 # explicitely with str and unicode instead of simply comparing 459 # with basestring. (at least on Python 2.5.1) 460 StringTypes = (str, unicode, UserString) 461
462 - def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
463 return isinstance(obj, DictTypes)
464
465 - def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
466 return isinstance(obj, ListTypes)
467
468 - def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
469 return isinstance(obj, SequenceTypes)
470
471 - def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
472 return isinstance(obj, tuple)
473
474 - def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
475 return isinstance(obj, StringTypes)
476
477 - def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
478 # Profiling shows that there is an impressive speed-up of 2x 479 # when explicitely checking for strings instead of just not 480 # sequence when the argument (i.e. obj) is already a string. 481 # But, if obj is a not string than it is twice as fast to 482 # check only for 'not sequence'. The following code therefore 483 # assumes that the obj argument is a string must of the time. 484 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
485
486 - def do_flatten(sequence, result, isinstance=isinstance, 487 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
488 for item in sequence: 489 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 490 result.append(item) 491 else: 492 do_flatten(item, result)
493
494 - def flatten(obj, isinstance=isinstance, StringTypes=StringTypes, 495 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
496 """Flatten a sequence to a non-nested list. 497 498 Flatten() converts either a single scalar or a nested sequence 499 to a non-nested list. Note that flatten() considers strings 500 to be scalars instead of sequences like Python would. 501 """ 502 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes): 503 return [obj] 504 result = [] 505 for item in obj: 506 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 507 result.append(item) 508 else: 509 do_flatten(item, result) 510 return result
511
512 - def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes, 513 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
514 """Flatten a sequence to a non-nested list. 515 516 Same as flatten(), but it does not handle the single scalar 517 case. This is slightly more efficient when one knows that 518 the sequence to flatten can not be a scalar. 519 """ 520 result = [] 521 for item in sequence: 522 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 523 result.append(item) 524 else: 525 do_flatten(item, result) 526 return result
527 528 529 # The SCons "semi-deep" copy. 530 # 531 # This makes separate copies of lists (including UserList objects) 532 # dictionaries (including UserDict objects) and tuples, but just copies 533 # references to anything else it finds. 534 # 535 # A special case is any object that has a __semi_deepcopy__() method, 536 # which we invoke to create the copy, which is used by the BuilderDict 537 # class because of its extra initialization argument. 538 # 539 # The dispatch table approach used here is a direct rip-off from the 540 # normal Python copy module. 541 542 _semi_deepcopy_dispatch = d = {} 543
544 -def _semi_deepcopy_dict(x):
545 copy = {} 546 for key, val in x.items(): 547 # The regular Python copy.deepcopy() also deepcopies the key, 548 # as follows: 549 # 550 # copy[semi_deepcopy(key)] = semi_deepcopy(val) 551 # 552 # Doesn't seem like we need to, but we'll comment it just in case. 553 copy[key] = semi_deepcopy(val) 554 return copy
555 d[types.DictionaryType] = _semi_deepcopy_dict 556
557 -def _semi_deepcopy_list(x):
558 return map(semi_deepcopy, x)
559 d[types.ListType] = _semi_deepcopy_list 560
561 -def _semi_deepcopy_tuple(x):
562 return tuple(map(semi_deepcopy, x))
563 d[types.TupleType] = _semi_deepcopy_tuple 564
565 -def _semi_deepcopy_inst(x):
566 if hasattr(x, '__semi_deepcopy__'): 567 return x.__semi_deepcopy__() 568 elif isinstance(x, UserDict): 569 return x.__class__(_semi_deepcopy_dict(x)) 570 elif isinstance(x, UserList): 571 return x.__class__(_semi_deepcopy_list(x)) 572 else: 573 return x
574 d[types.InstanceType] = _semi_deepcopy_inst 575
576 -def semi_deepcopy(x):
577 copier = _semi_deepcopy_dispatch.get(type(x)) 578 if copier: 579 return copier(x) 580 else: 581 return x
582 583 584
585 -class Proxy:
586 """A simple generic Proxy class, forwarding all calls to 587 subject. So, for the benefit of the python newbie, what does 588 this really mean? Well, it means that you can take an object, let's 589 call it 'objA', and wrap it in this Proxy class, with a statement 590 like this 591 592 proxyObj = Proxy(objA), 593 594 Then, if in the future, you do something like this 595 596 x = proxyObj.var1, 597 598 since Proxy does not have a 'var1' attribute (but presumably objA does), 599 the request actually is equivalent to saying 600 601 x = objA.var1 602 603 Inherit from this class to create a Proxy.""" 604
605 - def __init__(self, subject):
606 """Wrap an object as a Proxy object""" 607 self.__subject = subject
608
609 - def __getattr__(self, name):
610 """Retrieve an attribute from the wrapped object. If the named 611 attribute doesn't exist, AttributeError is raised""" 612 return getattr(self.__subject, name)
613
614 - def get(self):
615 """Retrieve the entire wrapped object""" 616 return self.__subject
617
618 - def __cmp__(self, other):
619 if issubclass(other.__class__, self.__subject.__class__): 620 return cmp(self.__subject, other) 621 return cmp(self.__dict__, other.__dict__)
622 623 # attempt to load the windows registry module: 624 can_read_reg = 0 625 try: 626 import _winreg 627 628 can_read_reg = 1 629 hkey_mod = _winreg 630 631 RegOpenKeyEx = _winreg.OpenKeyEx 632 RegEnumKey = _winreg.EnumKey 633 RegEnumValue = _winreg.EnumValue 634 RegQueryValueEx = _winreg.QueryValueEx 635 RegError = _winreg.error 636 637 except ImportError: 638 try: 639 import win32api 640 import win32con 641 can_read_reg = 1 642 hkey_mod = win32con 643 644 RegOpenKeyEx = win32api.RegOpenKeyEx 645 RegEnumKey = win32api.RegEnumKey 646 RegEnumValue = win32api.RegEnumValue 647 RegQueryValueEx = win32api.RegQueryValueEx 648 RegError = win32api.error 649 650 except ImportError:
651 - class _NoError(Exception):
652 pass
653 RegError = _NoError 654 655 if can_read_reg: 656 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT 657 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE 658 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER 659 HKEY_USERS = hkey_mod.HKEY_USERS 660
661 - def RegGetValue(root, key):
662 """This utility function returns a value in the registry 663 without having to open the key first. Only available on 664 Windows platforms with a version of Python that can read the 665 registry. Returns the same thing as 666 SCons.Util.RegQueryValueEx, except you just specify the entire 667 path to the value, and don't have to bother opening the key 668 first. So: 669 670 Instead of: 671 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, 672 r'SOFTWARE\Microsoft\Windows\CurrentVersion') 673 out = SCons.Util.RegQueryValueEx(k, 674 'ProgramFilesDir') 675 676 You can write: 677 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, 678 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir') 679 """ 680 # I would use os.path.split here, but it's not a filesystem 681 # path... 682 p = key.rfind('\\') + 1 683 keyp = key[:p] 684 val = key[p:] 685 k = RegOpenKeyEx(root, keyp) 686 return RegQueryValueEx(k,val)
687 688 if sys.platform == 'win32': 689
690 - def WhereIs(file, path=None, pathext=None, reject=[]):
691 if path is None: 692 try: 693 path = os.environ['PATH'] 694 except KeyError: 695 return None 696 if is_String(path): 697 path = string.split(path, os.pathsep) 698 if pathext is None: 699 try: 700 pathext = os.environ['PATHEXT'] 701 except KeyError: 702 pathext = '.COM;.EXE;.BAT;.CMD' 703 if is_String(pathext): 704 pathext = string.split(pathext, os.pathsep) 705 for ext in pathext: 706 if string.lower(ext) == string.lower(file[-len(ext):]): 707 pathext = [''] 708 break 709 if not is_List(reject) and not is_Tuple(reject): 710 reject = [reject] 711 for dir in path: 712 f = os.path.join(dir, file) 713 for ext in pathext: 714 fext = f + ext 715 if os.path.isfile(fext): 716 try: 717 reject.index(fext) 718 except ValueError: 719 return os.path.normpath(fext) 720 continue 721 return None
722 723 elif os.name == 'os2': 724
725 - def WhereIs(file, path=None, pathext=None, reject=[]):
726 if path is None: 727 try: 728 path = os.environ['PATH'] 729 except KeyError: 730 return None 731 if is_String(path): 732 path = string.split(path, os.pathsep) 733 if pathext is None: 734 pathext = ['.exe', '.cmd'] 735 for ext in pathext: 736 if string.lower(ext) == string.lower(file[-len(ext):]): 737 pathext = [''] 738 break 739 if not is_List(reject) and not is_Tuple(reject): 740 reject = [reject] 741 for dir in path: 742 f = os.path.join(dir, file) 743 for ext in pathext: 744 fext = f + ext 745 if os.path.isfile(fext): 746 try: 747 reject.index(fext) 748 except ValueError: 749 return os.path.normpath(fext) 750 continue 751 return None
752 753 else: 754
755 - def WhereIs(file, path=None, pathext=None, reject=[]):
756 import stat 757 if path is None: 758 try: 759 path = os.environ['PATH'] 760 except KeyError: 761 return None 762 if is_String(path): 763 path = string.split(path, os.pathsep) 764 if not is_List(reject) and not is_Tuple(reject): 765 reject = [reject] 766 for d in path: 767 f = os.path.join(d, file) 768 if os.path.isfile(f): 769 try: 770 st = os.stat(f) 771 except OSError: 772 # os.stat() raises OSError, not IOError if the file 773 # doesn't exist, so in this case we let IOError get 774 # raised so as to not mask possibly serious disk or 775 # network issues. 776 continue 777 if stat.S_IMODE(st[stat.ST_MODE]) & 0111: 778 try: 779 reject.index(f) 780 except ValueError: 781 return os.path.normpath(f) 782 continue 783 return None
784
785 -def PrependPath(oldpath, newpath, sep = os.pathsep):
786 """This prepends newpath elements to the given oldpath. Will only 787 add any particular path once (leaving the first one it encounters 788 and ignoring the rest, to preserve path order), and will 789 os.path.normpath and os.path.normcase all paths to help assure 790 this. This can also handle the case where the given old path 791 variable is a list instead of a string, in which case a list will 792 be returned instead of a string. 793 794 Example: 795 Old Path: "/foo/bar:/foo" 796 New Path: "/biz/boom:/foo" 797 Result: "/biz/boom:/foo:/foo/bar" 798 """ 799 800 orig = oldpath 801 is_list = 1 802 paths = orig 803 if not is_List(orig) and not is_Tuple(orig): 804 paths = string.split(paths, sep) 805 is_list = 0 806 807 if is_List(newpath) or is_Tuple(newpath): 808 newpaths = newpath 809 else: 810 newpaths = string.split(newpath, sep) 811 812 newpaths = newpaths + paths # prepend new paths 813 814 normpaths = [] 815 paths = [] 816 # now we add them only if they are unique 817 for path in newpaths: 818 normpath = os.path.normpath(os.path.normcase(path)) 819 if path and not normpath in normpaths: 820 paths.append(path) 821 normpaths.append(normpath) 822 823 if is_list: 824 return paths 825 else: 826 return string.join(paths, sep)
827
828 -def AppendPath(oldpath, newpath, sep = os.pathsep):
829 """This appends new path elements to the given old path. Will 830 only add any particular path once (leaving the last one it 831 encounters and ignoring the rest, to preserve path order), and 832 will os.path.normpath and os.path.normcase all paths to help 833 assure this. This can also handle the case where the given old 834 path variable is a list instead of a string, in which case a list 835 will be returned instead of a string. 836 837 Example: 838 Old Path: "/foo/bar:/foo" 839 New Path: "/biz/boom:/foo" 840 Result: "/foo/bar:/biz/boom:/foo" 841 """ 842 843 orig = oldpath 844 is_list = 1 845 paths = orig 846 if not is_List(orig) and not is_Tuple(orig): 847 paths = string.split(paths, sep) 848 is_list = 0 849 850 if is_List(newpath) or is_Tuple(newpath): 851 newpaths = newpath 852 else: 853 newpaths = string.split(newpath, sep) 854 855 newpaths = paths + newpaths # append new paths 856 newpaths.reverse() 857 858 normpaths = [] 859 paths = [] 860 # now we add them only of they are unique 861 for path in newpaths: 862 normpath = os.path.normpath(os.path.normcase(path)) 863 if path and not normpath in normpaths: 864 paths.append(path) 865 normpaths.append(normpath) 866 867 paths.reverse() 868 869 if is_list: 870 return paths 871 else: 872 return string.join(paths, sep)
873 874 if sys.platform == 'cygwin':
875 - def get_native_path(path):
876 """Transforms an absolute path into a native path for the system. In 877 Cygwin, this converts from a Cygwin path to a Windows one.""" 878 return string.replace(os.popen('cygpath -w ' + path).read(), '\n', '')
879 else:
880 - def get_native_path(path):
881 """Transforms an absolute path into a native path for the system. 882 Non-Cygwin version, just leave the path alone.""" 883 return path
884 885 display = DisplayEngine() 886
887 -def Split(arg):
888 if is_List(arg) or is_Tuple(arg): 889 return arg 890 elif is_String(arg): 891 return string.split(arg) 892 else: 893 return [arg]
894
895 -class CLVar(UserList):
896 """A class for command-line construction variables. 897 898 This is a list that uses Split() to split an initial string along 899 white-space arguments, and similarly to split any strings that get 900 added. This allows us to Do the Right Thing with Append() and 901 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1 902 arg2') regardless of whether a user adds a list or a string to a 903 command-line construction variable. 904 """
905 - def __init__(self, seq = []):
906 UserList.__init__(self, Split(seq))
907 - def __coerce__(self, other):
908 return (self, CLVar(other))
909 - def __str__(self):
910 return string.join(self.data)
911 912 # A dictionary that preserves the order in which items are added. 913 # Submitted by David Benjamin to ActiveState's Python Cookbook web site: 914 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 915 # Including fixes/enhancements from the follow-on discussions.
916 -class OrderedDict(UserDict):
917 - def __init__(self, dict = None):
918 self._keys = [] 919 UserDict.__init__(self, dict)
920
921 - def __delitem__(self, key):
922 UserDict.__delitem__(self, key) 923 self._keys.remove(key)
924
925 - def __setitem__(self, key, item):
926 UserDict.__setitem__(self, key, item) 927 if key not in self._keys: self._keys.append(key)
928
929 - def clear(self):
930 UserDict.clear(self) 931 self._keys = []
932
933 - def copy(self):
934 dict = OrderedDict() 935 dict.update(self) 936 return dict
937
938 - def items(self):
939 return zip(self._keys, self.values())
940
941 - def keys(self):
942 return self._keys[:]
943
944 - def popitem(self):
945 try: 946 key = self._keys[-1] 947 except IndexError: 948 raise KeyError('dictionary is empty') 949 950 val = self[key] 951 del self[key] 952 953 return (key, val)
954
955 - def setdefault(self, key, failobj = None):
956 UserDict.setdefault(self, key, failobj) 957 if key not in self._keys: self._keys.append(key)
958
959 - def update(self, dict):
960 for (key, val) in dict.items(): 961 self.__setitem__(key, val)
962
963 - def values(self):
964 return map(self.get, self._keys)
965
966 -class Selector(OrderedDict):
967 """A callable ordered dictionary that maps file suffixes to 968 dictionary values. We preserve the order in which items are added 969 so that get_suffix() calls always return the first suffix added."""
970 - def __call__(self, env, source):
971 try: 972 ext = source[0].suffix 973 except IndexError: 974 ext = "" 975 try: 976 return self[ext] 977 except KeyError: 978 # Try to perform Environment substitution on the keys of 979 # the dictionary before giving up. 980 s_dict = {} 981 for (k,v) in self.items(): 982 if not k is None: 983 s_k = env.subst(k) 984 if s_dict.has_key(s_k): 985 # We only raise an error when variables point 986 # to the same suffix. If one suffix is literal 987 # and a variable suffix contains this literal, 988 # the literal wins and we don't raise an error. 989 raise KeyError, (s_dict[s_k][0], k, s_k) 990 s_dict[s_k] = (k,v) 991 try: 992 return s_dict[ext][1] 993 except KeyError: 994 try: 995 return self[None] 996 except KeyError: 997 return None
998 999 1000 if sys.platform == 'cygwin': 1001 # On Cygwin, os.path.normcase() lies, so just report back the 1002 # fact that the underlying Windows OS is case-insensitive.
1003 - def case_sensitive_suffixes(s1, s2):
1004 return 0
1005 else:
1006 - def case_sensitive_suffixes(s1, s2):
1007 return (os.path.normcase(s1) != os.path.normcase(s2))
1008
1009 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1010 if pre: 1011 path, fn = os.path.split(os.path.normpath(fname)) 1012 if fn[:len(pre)] != pre: 1013 fname = os.path.join(path, pre + fn) 1014 # Only append a suffix if the suffix we're going to add isn't already 1015 # there, and if either we've been asked to ensure the specific suffix 1016 # is present or there's no suffix on it at all. 1017 if suf and fname[-len(suf):] != suf and \ 1018 (ensure_suffix or not splitext(fname)[1]): 1019 fname = fname + suf 1020 return fname
1021 1022 1023 1024 # From Tim Peters, 1025 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1026 # ASPN: Python Cookbook: Remove duplicates from a sequence 1027 # (Also in the printed Python Cookbook.) 1028
1029 -def unique(s):
1030 """Return a list of the elements in s, but without duplicates. 1031 1032 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 1033 unique("abcabc") some permutation of ["a", "b", "c"], and 1034 unique(([1, 2], [2, 3], [1, 2])) some permutation of 1035 [[2, 3], [1, 2]]. 1036 1037 For best speed, all sequence elements should be hashable. Then 1038 unique() will usually work in linear time. 1039 1040 If not possible, the sequence elements should enjoy a total 1041 ordering, and if list(s).sort() doesn't raise TypeError it's 1042 assumed that they do enjoy a total ordering. Then unique() will 1043 usually work in O(N*log2(N)) time. 1044 1045 If that's not possible either, the sequence elements must support 1046 equality-testing. Then unique() will usually work in quadratic 1047 time. 1048 """ 1049 1050 n = len(s) 1051 if n == 0: 1052 return [] 1053 1054 # Try using a dict first, as that's the fastest and will usually 1055 # work. If it doesn't work, it will usually fail quickly, so it 1056 # usually doesn't cost much to *try* it. It requires that all the 1057 # sequence elements be hashable, and support equality comparison. 1058 u = {} 1059 try: 1060 for x in s: 1061 u[x] = 1 1062 except TypeError: 1063 pass # move on to the next method 1064 else: 1065 return u.keys() 1066 del u 1067 1068 # We can't hash all the elements. Second fastest is to sort, 1069 # which brings the equal elements together; then duplicates are 1070 # easy to weed out in a single pass. 1071 # NOTE: Python's list.sort() was designed to be efficient in the 1072 # presence of many duplicate elements. This isn't true of all 1073 # sort functions in all languages or libraries, so this approach 1074 # is more effective in Python than it may be elsewhere. 1075 try: 1076 t = list(s) 1077 t.sort() 1078 except TypeError: 1079 pass # move on to the next method 1080 else: 1081 assert n > 0 1082 last = t[0] 1083 lasti = i = 1 1084 while i < n: 1085 if t[i] != last: 1086 t[lasti] = last = t[i] 1087 lasti = lasti + 1 1088 i = i + 1 1089 return t[:lasti] 1090 del t 1091 1092 # Brute force is all that's left. 1093 u = [] 1094 for x in s: 1095 if x not in u: 1096 u.append(x) 1097 return u
1098 1099 1100 1101 # From Alex Martelli, 1102 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1103 # ASPN: Python Cookbook: Remove duplicates from a sequence 1104 # First comment, dated 2001/10/13. 1105 # (Also in the printed Python Cookbook.) 1106
1107 -def uniquer(seq, idfun=None):
1108 if idfun is None: 1109 def idfun(x): return x 1110 seen = {} 1111 result = [] 1112 for item in seq: 1113 marker = idfun(item) 1114 # in old Python versions: 1115 # if seen.has_key(marker) 1116 # but in new ones: 1117 if marker in seen: continue 1118 seen[marker] = 1 1119 result.append(item) 1120 return result
1121 1122 # A more efficient implementation of Alex's uniquer(), this avoids the 1123 # idfun() argument and function-call overhead by assuming that all 1124 # items in the sequence are hashable. 1125
1126 -def uniquer_hashables(seq):
1127 seen = {} 1128 result = [] 1129 for item in seq: 1130 #if not item in seen: 1131 if not seen.has_key(item): 1132 seen[item] = 1 1133 result.append(item) 1134 return result
1135 1136 1137 1138 # Much of the logic here was originally based on recipe 4.9 from the 1139 # Python CookBook, but we had to dumb it way down for Python 1.5.2.
1140 -class LogicalLines:
1141
1142 - def __init__(self, fileobj):
1143 self.fileobj = fileobj
1144
1145 - def readline(self):
1146 result = [] 1147 while 1: 1148 line = self.fileobj.readline() 1149 if not line: 1150 break 1151 if line[-2:] == '\\\n': 1152 result.append(line[:-2]) 1153 else: 1154 result.append(line) 1155 break 1156 return string.join(result, '')
1157
1158 - def readlines(self):
1159 result = [] 1160 while 1: 1161 line = self.readline() 1162 if not line: 1163 break 1164 result.append(line) 1165 return result
1166 1167 1168
1169 -class UniqueList(UserList):
1170 - def __init__(self, seq = []):
1171 UserList.__init__(self, seq) 1172 self.unique = True
1173 - def __make_unique(self):
1174 if not self.unique: 1175 self.data = uniquer_hashables(self.data) 1176 self.unique = True
1177 - def __lt__(self, other):
1178 self.__make_unique() 1179 return UserList.__lt__(self, other)
1180 - def __le__(self, other):
1181 self.__make_unique() 1182 return UserList.__le__(self, other)
1183 - def __eq__(self, other):
1184 self.__make_unique() 1185 return UserList.__eq__(self, other)
1186 - def __ne__(self, other):
1187 self.__make_unique() 1188 return UserList.__ne__(self, other)
1189 - def __gt__(self, other):
1190 self.__make_unique() 1191 return UserList.__gt__(self, other)
1192 - def __ge__(self, other):
1193 self.__make_unique() 1194 return UserList.__ge__(self, other)
1195 - def __cmp__(self, other):
1196 self.__make_unique() 1197 return UserList.__cmp__(self, other)
1198 - def __len__(self):
1199 self.__make_unique() 1200 return UserList.__len__(self)
1201 - def __getitem__(self, i):
1202 self.__make_unique() 1203 return UserList.__getitem__(self, i)
1204 - def __setitem__(self, i, item):
1205 UserList.__setitem__(self, i, item) 1206 self.unique = False
1207 - def __getslice__(self, i, j):
1208 self.__make_unique() 1209 return UserList.__getslice__(self, i, j)
1210 - def __setslice__(self, i, j, other):
1211 UserList.__setslice__(self, i, j, other) 1212 self.unique = False
1213 - def __add__(self, other):
1214 result = UserList.__add__(self, other) 1215 result.unique = False 1216 return result
1217 - def __radd__(self, other):
1218 result = UserList.__radd__(self, other) 1219 result.unique = False 1220 return result
1221 - def __iadd__(self, other):
1222 result = UserList.__iadd__(self, other) 1223 result.unique = False 1224 return result
1225 - def __mul__(self, other):
1226 result = UserList.__mul__(self, other) 1227 result.unique = False 1228 return result
1229 - def __rmul__(self, other):
1230 result = UserList.__rmul__(self, other) 1231 result.unique = False 1232 return result
1233 - def __imul__(self, other):
1234 result = UserList.__imul__(self, other) 1235 result.unique = False 1236 return result
1237 - def append(self, item):
1238 UserList.append(self, item) 1239 self.unique = False
1240 - def insert(self, i):
1241 UserList.insert(self, i) 1242 self.unique = False
1243 - def count(self, item):
1244 self.__make_unique() 1245 return UserList.count(self, item)
1246 - def index(self, item):
1247 self.__make_unique() 1248 return UserList.index(self, item)
1249 - def reverse(self):
1250 self.__make_unique() 1251 UserList.reverse(self)
1252 - def sort(self, *args, **kwds):
1253 self.__make_unique() 1254 #return UserList.sort(self, *args, **kwds) 1255 return apply(UserList.sort, (self,)+args, kwds)
1256 - def extend(self, other):
1257 UserList.extend(self, other) 1258 self.unique = False
1259 1260 1261
1262 -class Unbuffered:
1263 """ 1264 A proxy class that wraps a file object, flushing after every write, 1265 and delegating everything else to the wrapped object. 1266 """
1267 - def __init__(self, file):
1268 self.file = file
1269 - def write(self, arg):
1270 self.file.write(arg) 1271 self.file.flush()
1272 - def __getattr__(self, attr):
1273 return getattr(self.file, attr)
1274
1275 -def make_path_relative(path):
1276 """ makes an absolute path name to a relative pathname. 1277 """ 1278 if os.path.isabs(path): 1279 drive_s,path = os.path.splitdrive(path) 1280 1281 import re 1282 if not drive_s: 1283 path=re.compile("/*(.*)").findall(path)[0] 1284 else: 1285 path=path[1:] 1286 1287 assert( not os.path.isabs( path ) ), path 1288 return path
1289 1290 1291 1292 # The original idea for AddMethod() and RenameFunction() come from the 1293 # following post to the ActiveState Python Cookbook: 1294 # 1295 # ASPN: Python Cookbook : Install bound methods in an instance 1296 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 1297 # 1298 # That code was a little fragile, though, so the following changes 1299 # have been wrung on it: 1300 # 1301 # * Switched the installmethod() "object" and "function" arguments, 1302 # so the order reflects that the left-hand side is the thing being 1303 # "assigned to" and the right-hand side is the value being assigned. 1304 # 1305 # * Changed explicit type-checking to the "try: klass = object.__class__" 1306 # block in installmethod() below so that it still works with the 1307 # old-style classes that SCons uses. 1308 # 1309 # * Replaced the by-hand creation of methods and functions with use of 1310 # the "new" module, as alluded to in Alex Martelli's response to the 1311 # following Cookbook post: 1312 # 1313 # ASPN: Python Cookbook : Dynamically added methods to a class 1314 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 1315
1316 -def AddMethod(object, function, name = None):
1317 """ 1318 Adds either a bound method to an instance or an unbound method to 1319 a class. If name is ommited the name of the specified function 1320 is used by default. 1321 Example: 1322 a = A() 1323 def f(self, x, y): 1324 self.z = x + y 1325 AddMethod(f, A, "add") 1326 a.add(2, 4) 1327 print a.z 1328 AddMethod(lambda self, i: self.l[i], a, "listIndex") 1329 print a.listIndex(5) 1330 """ 1331 import new 1332 1333 if name is None: 1334 name = function.func_name 1335 else: 1336 function = RenameFunction(function, name) 1337 1338 try: 1339 klass = object.__class__ 1340 except AttributeError: 1341 # "object" is really a class, so it gets an unbound method. 1342 object.__dict__[name] = new.instancemethod(function, None, object) 1343 else: 1344 # "object" is really an instance, so it gets a bound method. 1345 object.__dict__[name] = new.instancemethod(function, object, klass)
1346
1347 -def RenameFunction(function, name):
1348 """ 1349 Returns a function identical to the specified function, but with 1350 the specified name. 1351 """ 1352 import new 1353 1354 # Compatibility for Python 1.5 and 2.1. Can be removed in favor of 1355 # passing function.func_defaults directly to new.function() once 1356 # we base on Python 2.2 or later. 1357 func_defaults = function.func_defaults 1358 if func_defaults is None: 1359 func_defaults = () 1360 1361 return new.function(function.func_code, 1362 function.func_globals, 1363 name, 1364 func_defaults)
1365 1366 1367 md5 = False
1368 -def MD5signature(s):
1369 return str(s)
1370 1371 try: 1372 import hashlib 1373 except ImportError: 1374 pass 1375 else: 1376 if hasattr(hashlib, 'md5'): 1377 md5 = True
1378 - def MD5signature(s):
1379 m = hashlib.md5() 1380 m.update(str(s)) 1381 return m.hexdigest()
1382
1383 -def MD5collect(signatures):
1384 """ 1385 Collects a list of signatures into an aggregate signature. 1386 1387 signatures - a list of signatures 1388 returns - the aggregate signature 1389 """ 1390 if len(signatures) == 1: 1391 return signatures[0] 1392 else: 1393 return MD5signature(string.join(signatures, ', '))
1394 1395 1396 1397 # From Dinu C. Gherman, 1398 # Python Cookbook, second edition, recipe 6.17, p. 277. 1399 # Also: 1400 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 1401 # ASPN: Python Cookbook: Null Object Design Pattern 1402
1403 -class Null:
1404 """ Null objects always and reliably "do nothging." """ 1405
1406 - def __new__(cls, *args, **kwargs):
1407 if not '_inst' in vars(cls): 1408 #cls._inst = type.__new__(cls, *args, **kwargs) 1409 cls._inst = apply(type.__new__, (cls,) + args, kwargs) 1410 return cls._inst
1411 - def __init__(self, *args, **kwargs):
1412 pass
1413 - def __call__(self, *args, **kwargs):
1414 return self
1415 - def __repr__(self):
1416 return "Null()"
1417 - def __nonzero__(self):
1418 return False
1419 - def __getattr__(self, mname):
1420 return self
1421 - def __setattr__(self, name, value):
1422 return self
1423 - def __delattr__(self, name):
1424 return self
1425 1426 1427 1428 del __revision__ 1429