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