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