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