Add some documentation to the SCons-version-switching hack
[senf.git] / scons / scons-1.2.0 / engine / SCons / Util.py
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
240 def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited={}):
241     """
242     Print a tree of nodes.  This is like render_tree, except it prints
243     lines directly instead of creating a string representation in memory,
244     so that huge trees can be printed.
245
246     root - the root node of the tree
247     child_func - the function called to get the children of a node
248     prune - don't visit the same node twice
249     showtags - print status information to the left of each node line
250     margin - the format of the left margin to use for children of root.
251        1 results in a pipe, and 0 results in no pipe.
252     visited - a dictionary of visited nodes in the current branch if not prune,
253        or in the whole tree if prune.
254     """
255
256     rname = str(root)
257
258     if showtags:
259
260         if showtags == 2:
261             print ' E         = exists'
262             print '  R        = exists in repository only'
263             print '   b       = implicit builder'
264             print '   B       = explicit builder'
265             print '    S      = side effect'
266             print '     P     = precious'
267             print '      A    = always build'
268             print '       C   = current'
269             print '        N  = no clean'
270             print '         H = no cache'
271             print ''
272
273         tags = ['[']
274         tags.append(' E'[IDX(root.exists())])
275         tags.append(' R'[IDX(root.rexists() and not root.exists())])
276         tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
277                            [0,2][IDX(root.has_builder())]])
278         tags.append(' S'[IDX(root.side_effect)])
279         tags.append(' P'[IDX(root.precious)])
280         tags.append(' A'[IDX(root.always_build)])
281         tags.append(' C'[IDX(root.is_up_to_date())])
282         tags.append(' N'[IDX(root.noclean)])
283         tags.append(' H'[IDX(root.nocache)])
284         tags.append(']')
285
286     else:
287         tags = []
288
289     def MMM(m):
290         return ["  ","| "][m]
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__