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