SCons.Util

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
     267 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
     268      """
     269      Print a tree of nodes.  This is like render_tree, except it prints
     270      lines directly instead of creating a string representation in memory,
     271      so that huge trees can be printed.
     272
     273      root - the root node of the tree
     274      child_func - the function called to get the children of a node
     275      prune - don't visit the same node twice
     276      showtags - print status information to the left of each node line
     277      margin - the format of the left margin to use for children of root.
     278         1 results in a pipe, and 0 results in no pipe.
     279      visited - a dictionary of visited nodes in the current branch if not prune,
     280         or in the whole tree if prune.
     281      """
     282
     283      rname = str(root)
     284
     285      if showtags:
     286
     287          if showtags == 2:
     288              print ' E         = exists'
     289              print '  R        = exists in repository only'
     290              print '   b       = implicit builder'
     291              print '   B       = explicit builder'
     292              print '    S      = side effect'
     293              print '     P     = precious'
     294              print '      A    = always build'
     295              print '       C   = current'
     296              print '        N  = no clean'
     297              print '         H = no cache'
     298              print ''
     299
     300          tags = ['[']
     301          tags.append(' E'[IDX(root.exists())])
     302          tags.append(' R'[IDX(root.rexists() and not root.exists())])
     303          tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
     304                             [0,2][IDX(root.has_builder())]])
     305          tags.append(' S'[IDX(root.side_effect)])
     306          tags.append(' P'[IDX(root.precious)])
     307          tags.append(' A'[IDX(root.always_build)])
     308          tags.append(' C'[IDX(root.is_up_to_date())])
     309          tags.append(' N'[IDX(root.noclean)])
     310          tags.append(' H'[IDX(root.nocache)])
     311          tags.append(']')
     312
     313      else:
     314          tags = []
     315
     316      def MMM(m):
     317          return ["  ","| "][m]
     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