Package SCons :: Module Util
[hide private]
[frames] | no frames]

Source Code for Module SCons.Util

   1  """SCons.Util 
   2   
   3  Various utility functions go here. 
   4   
   5  """ 
   6   
   7  # 
   8  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
   9  # 
  10  # Permission is hereby granted, free of charge, to any person obtaining 
  11  # a copy of this software and associated documentation files (the 
  12  # "Software"), to deal in the Software without restriction, including 
  13  # without limitation the rights to use, copy, modify, merge, publish, 
  14  # distribute, sublicense, and/or sell copies of the Software, and to 
  15  # permit persons to whom the Software is furnished to do so, subject to 
  16  # the following conditions: 
  17  # 
  18  # The above copyright notice and this permission notice shall be included 
  19  # in all copies or substantial portions of the Software. 
  20  # 
  21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  22  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  23  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  24  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  25  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  26  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  27  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  28  # 
  29   
  30  __revision__ = "src/engine/SCons/Util.py 3795 2008/11/25 22:04:43 scons" 
  31   
  32  import copy 
  33  import os 
  34  import os.path 
  35  import re 
  36  import string 
  37  import sys 
  38  import types 
  39   
  40  from UserDict import UserDict 
  41  from UserList import UserList 
  42  from UserString import UserString 
  43   
  44  # Don't "from types import ..." these because we need to get at the 
  45  # types module later to look for UnicodeType. 
  46  DictType        = types.DictType 
  47  InstanceType    = types.InstanceType 
  48  ListType        = types.ListType 
  49  StringType      = types.StringType 
  50  TupleType       = types.TupleType 
  51   
52 -def dictify(keys, values, result={}):
53 for k, v in zip(keys, values): 54 result[k] = v 55 return result
56 57 _altsep = os.altsep 58 if _altsep is None and sys.platform == 'win32': 59 # My ActivePython 2.0.1 doesn't set os.altsep! What gives? 60 _altsep = '/' 61 if _altsep:
62 - def rightmost_separator(path, sep, _altsep=_altsep):
63 rfind = string.rfind 64 return max(rfind(path, sep), rfind(path, _altsep))
65 else: 66 rightmost_separator = string.rfind 67 68 # First two from the Python Cookbook, just for completeness. 69 # (Yeah, yeah, YAGNI...)
70 -def containsAny(str, set):
71 """Check whether sequence str contains ANY of the items in set.""" 72 for c in set: 73 if c in str: return 1 74 return 0
75
76 -def containsAll(str, set):
77 """Check whether sequence str contains ALL of the items in set.""" 78 for c in set: 79 if c not in str: return 0 80 return 1
81
82 -def containsOnly(str, set):
83 """Check whether sequence str contains ONLY items in set.""" 84 for c in str: 85 if c not in set: return 0 86 return 1
87
88 -def splitext(path):
89 "Same as os.path.splitext() but faster." 90 sep = rightmost_separator(path, os.sep) 91 dot = string.rfind(path, '.') 92 # An ext is only real if it has at least one non-digit char 93 if dot > sep and not containsOnly(path[dot:], "0123456789."): 94 return path[:dot],path[dot:] 95 else: 96 return path,""
97
98 -def updrive(path):
99 """ 100 Make the drive letter (if any) upper case. 101 This is useful because Windows is inconsitent on the case 102 of the drive letter, which can cause inconsistencies when 103 calculating command signatures. 104 """ 105 drive, rest = os.path.splitdrive(path) 106 if drive: 107 path = string.upper(drive) + rest 108 return path
109
110 -class CallableComposite(UserList):
111 """A simple composite callable class that, when called, will invoke all 112 of its contained callables with the same arguments."""
113 - def __call__(self, *args, **kwargs):
114 retvals = map(lambda x, args=args, kwargs=kwargs: apply(x, 115 args, 116 kwargs), 117 self.data) 118 if self.data and (len(self.data) == len(filter(callable, retvals))): 119 return self.__class__(retvals) 120 return NodeList(retvals)
121
122 -class NodeList(UserList):
123 """This class is almost exactly like a regular list of Nodes 124 (actually it can hold any object), with one important difference. 125 If you try to get an attribute from this list, it will return that 126 attribute from every item in the list. For example: 127 128 >>> someList = NodeList([ ' foo ', ' bar ' ]) 129 >>> someList.strip() 130 [ 'foo', 'bar' ] 131 """
132 - def __nonzero__(self):
133 return len(self.data) != 0
134
135 - def __str__(self):
136 return string.join(map(str, self.data))
137
138 - def __getattr__(self, name):
139 if not self.data: 140 # If there is nothing in the list, then we have no attributes to 141 # pass through, so raise AttributeError for everything. 142 raise AttributeError, "NodeList has no attribute: %s" % name 143 144 # Return a list of the attribute, gotten from every element 145 # in the list 146 attrList = map(lambda x, n=name: getattr(x, n), self.data) 147 148 # Special case. If the attribute is callable, we do not want 149 # to return a list of callables. Rather, we want to return a 150 # single callable that, when called, will invoke the function on 151 # all elements of this list. 152 if self.data and (len(self.data) == len(filter(callable, attrList))): 153 return CallableComposite(attrList) 154 return self.__class__(attrList)
155 156 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') 157
158 -def get_environment_var(varstr):
159 """Given a string, first determine if it looks like a reference 160 to a single environment variable, like "$FOO" or "${FOO}". 161 If so, return that variable with no decorations ("FOO"). 162 If not, return None.""" 163 mo=_get_env_var.match(to_String(varstr)) 164 if mo: 165 var = mo.group(1) 166 if var[0] == '{': 167 return var[1:-1] 168 else: 169 return var 170 else: 171 return None
172
173 -class DisplayEngine:
174 - def __init__(self):
175 self.__call__ = self.print_it
176
177 - def print_it(self, text, append_newline=1):
178 if append_newline: text = text + '\n' 179 try: 180 sys.stdout.write(text) 181 except IOError: 182 # Stdout might be connected to a pipe that has been closed 183 # by now. The most likely reason for the pipe being closed 184 # is that the user has press ctrl-c. It this is the case, 185 # then SCons is currently shutdown. We therefore ignore 186 # IOError's here so that SCons can continue and shutdown 187 # properly so that the .sconsign is correctly written 188 # before SCons exits. 189 pass
190
191 - def dont_print(self, text, append_newline=1):
192 pass
193
194 - def set_mode(self, mode):
195 if mode: 196 self.__call__ = self.print_it 197 else: 198 self.__call__ = self.dont_print
199
200 -def render_tree(root, child_func, prune=0, margin=[0], visited={}):
201 """ 202 Render a tree of nodes into an ASCII tree view. 203 root - the root node of the tree 204 child_func - the function called to get the children of a node 205 prune - don't visit the same node twice 206 margin - the format of the left margin to use for children of root. 207 1 results in a pipe, and 0 results in no pipe. 208 visited - a dictionary of visited nodes in the current branch if not prune, 209 or in the whole tree if prune. 210 """ 211 212 rname = str(root) 213 214 children = child_func(root) 215 retval = "" 216 for pipe in margin[:-1]: 217 if pipe: 218 retval = retval + "| " 219 else: 220 retval = retval + " " 221 222 if visited.has_key(rname): 223 return retval + "+-[" + rname + "]\n" 224 225 retval = retval + "+-" + rname + "\n" 226 if not prune: 227 visited = copy.copy(visited) 228 visited[rname] = 1 229 230 for i in range(len(children)): 231 margin.append(i<len(children)-1) 232 retval = retval + render_tree(children[i], child_func, prune, margin, visited 233 ) 234 margin.pop() 235 236 return retval
237 238 IDX = lambda N: N and 1 or 0 239 291 margins = map(MMM, margin[:-1]) 292 293 children = child_func(root) 294 295 if prune and visited.has_key(rname) and children: 296 print string.join(tags + margins + ['+-[', rname, ']'], '') 297 return 298 299 print string.join(tags + margins + ['+-', rname], '') 300 301 visited[rname] = 1 302 303 if children: 304 margin.append(1) 305 map(lambda C, cf=child_func, p=prune, i=IDX(showtags), m=margin, v=visited: 306 print_tree(C, cf, p, i, m, v), 307 children[:-1]) 308 margin[-1] = 0 309 print_tree(children[-1], child_func, prune, IDX(showtags), margin, visited) 310 margin.pop() 311 312 313 314 # Functions for deciding if things are like various types, mainly to 315 # handle UserDict, UserList and UserString like their underlying types. 316 # 317 # Yes, all of this manual testing breaks polymorphism, and the real 318 # Pythonic way to do all of this would be to just try it and handle the 319 # exception, but handling the exception when it's not the right type is 320 # often too slow. 321 322 try:
323 - class mystr(str):
324 pass
325 except TypeError: 326 # An older Python version without new-style classes. 327 # 328 # The actual implementations here have been selected after timings 329 # coded up in in bench/is_types.py (from the SCons source tree, 330 # see the scons-src distribution), mostly against Python 1.5.2. 331 # Key results from those timings: 332 # 333 # -- Storing the type of the object in a variable (t = type(obj)) 334 # slows down the case where it's a native type and the first 335 # comparison will match, but nicely speeds up the case where 336 # it's a different native type. Since that's going to be 337 # common, it's a good tradeoff. 338 # 339 # -- The data show that calling isinstance() on an object that's 340 # a native type (dict, list or string) is expensive enough 341 # that checking up front for whether the object is of type 342 # InstanceType is a pretty big win, even though it does slow 343 # down the case where it really *is* an object instance a 344 # little bit.
345 - def is_Dict(obj):
346 t = type(obj) 347 return t is DictType or \ 348 (t is InstanceType and isinstance(obj, UserDict))
349
350 - def is_List(obj):
351 t = type(obj) 352 return t is ListType \ 353 or (t is InstanceType and isinstance(obj, UserList))
354
355 - def is_Sequence(obj):
356 t = type(obj) 357 return t is ListType \ 358 or t is TupleType \ 359 or (t is InstanceType and isinstance(obj, UserList))
360
361 - def is_Tuple(obj):
362 t = type(obj) 363 return t is TupleType
364 365 if hasattr(types, 'UnicodeType'):
366 - def is_String(obj):
367 t = type(obj) 368 return t is StringType \ 369 or t is UnicodeType \ 370 or (t is InstanceType and isinstance(obj, UserString))
371 else:
372 - def is_String(obj):
373 t = type(obj)