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