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