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