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 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 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 return ' '.join([to_String_for_subst(e) for e in s]) 485 elif isinstance(s, UserString): 486 # s.data can only be either a unicode or a regular 487 # string. Please see the UserString initializer. 488 return s.data 489 else: 490 return str(s)
491
492 -def to_String_for_signature(obj, to_String_for_subst=to_String_for_subst, 493 AttributeError=AttributeError):
494 try: 495 f = obj.for_signature 496 except AttributeError: 497 if isinstance(obj, dict): 498 # pprint will output dictionary in key sorted order 499 # with py3.5 the order was randomized. In general depending on dictionary order 500 # which was undefined until py3.6 (where it's by insertion order) was not wise. 501 return pprint.pformat(obj, width=1000000) 502 else: 503 return to_String_for_subst(obj) 504 else: 505 return f()
506 507 508 # The SCons "semi-deep" copy. 509 # 510 # This makes separate copies of lists (including UserList objects) 511 # dictionaries (including UserDict objects) and tuples, but just copies 512 # references to anything else it finds. 513 # 514 # A special case is any object that has a __semi_deepcopy__() method, 515 # which we invoke to create the copy. Currently only used by 516 # BuilderDict to actually prevent the copy operation (as invalid on that object). 517 # 518 # The dispatch table approach used here is a direct rip-off from the 519 # normal Python copy module. 520 521 _semi_deepcopy_dispatch = d = {} 522
523 -def semi_deepcopy_dict(x, exclude = [] ):
524 copy = {} 525 for key, val in x.items(): 526 # The regular Python copy.deepcopy() also deepcopies the key, 527 # as follows: 528 # 529 # copy[semi_deepcopy(key)] = semi_deepcopy(val) 530 # 531 # Doesn't seem like we need to, but we'll comment it just in case. 532 if key not in exclude: 533 copy[key] = semi_deepcopy(val) 534 return copy
535 d[dict] = semi_deepcopy_dict 536
537 -def _semi_deepcopy_list(x):
538 return list(map(semi_deepcopy, x))
539 d[list] = _semi_deepcopy_list 540
541 -def _semi_deepcopy_tuple(x):
542 return tuple(map(semi_deepcopy, x))
543 d[tuple] = _semi_deepcopy_tuple 544
545 -def semi_deepcopy(x):
546 copier = _semi_deepcopy_dispatch.get(type(x)) 547 if copier: 548 return copier(x) 549 else: 550 if hasattr(x, '__semi_deepcopy__') and callable(x.__semi_deepcopy__): 551 return x.__semi_deepcopy__() 552 elif isinstance(x, UserDict): 553 return x.__class__(semi_deepcopy_dict(x)) 554 elif isinstance(x, UserList): 555 return x.__class__(_semi_deepcopy_list(x)) 556 557 return x
558 559
560 -class Proxy(object):
561 """A simple generic Proxy class, forwarding all calls to 562 subject. So, for the benefit of the python newbie, what does 563 this really mean? Well, it means that you can take an object, let's 564 call it 'objA', and wrap it in this Proxy class, with a statement 565 like this 566 567 proxyObj = Proxy(objA), 568 569 Then, if in the future, you do something like this 570 571 x = proxyObj.var1, 572 573 since Proxy does not have a 'var1' attribute (but presumably objA does), 574 the request actually is equivalent to saying 575 576 x = objA.var1 577 578 Inherit from this class to create a Proxy. 579 580 Note that, with new-style classes, this does *not* work transparently 581 for Proxy subclasses that use special .__*__() method names, because 582 those names are now bound to the class, not the individual instances. 583 You now need to know in advance which .__*__() method names you want 584 to pass on to the underlying Proxy object, and specifically delegate 585 their calls like this: 586 587 class Foo(Proxy): 588 __str__ = Delegate('__str__') 589 """ 590
591 - def __init__(self, subject):
592 """Wrap an object as a Proxy object""" 593 self._subject = subject
594
595 - def __getattr__(self, name):
596 """Retrieve an attribute from the wrapped object. If the named 597 attribute doesn't exist, AttributeError is raised""" 598 return getattr(self._subject, name)
599
600 - def get(self):
601 """Retrieve the entire wrapped object""" 602 return self._subject
603
604 - def __eq__(self, other):
605 if issubclass(other.__class__, self._subject.__class__): 606 return self._subject == other 607 return self.__dict__ == other.__dict__
608
609 -class Delegate(object):
610 """A Python Descriptor class that delegates attribute fetches 611 to an underlying wrapped subject of a Proxy. Typical use: 612 613 class Foo(Proxy): 614 __str__ = Delegate('__str__') 615 """
616 - def __init__(self, attribute):
617 self.attribute = attribute
618 - def __get__(self, obj, cls):
619 if isinstance(obj, cls): 620 return getattr(obj._subject, self.attribute) 621 else: 622 return self
623 624 # attempt to load the windows registry module: 625 can_read_reg = 0 626 try: 627 import winreg 628 629 can_read_reg = 1 630 hkey_mod = winreg 631 632 RegOpenKeyEx = winreg.OpenKeyEx 633 RegEnumKey = winreg.EnumKey 634 RegEnumValue = winreg.EnumValue 635 RegQueryValueEx = winreg.QueryValueEx 636 RegError = winreg.error 637 638 except ImportError: 639 try: 640 import win32api 641 import win32con 642 can_read_reg = 1 643 hkey_mod = win32con 644 645 RegOpenKeyEx = win32api.RegOpenKeyEx 646 RegEnumKey = win32api.RegEnumKey 647 RegEnumValue = win32api.RegEnumValue 648 RegQueryValueEx = win32api.RegQueryValueEx 649 RegError = win32api.error 650 651 except ImportError:
652 - class _NoError(Exception):
653 pass
654 RegError = _NoError 655 656 WinError = None 657 # Make sure we have a definition of WindowsError so we can 658 # run platform-independent tests of Windows functionality on 659 # platforms other than Windows. (WindowsError is, in fact, an 660 # OSError subclass on Windows.)
661 -class PlainWindowsError(OSError):
662 pass
663 try: 664 WinError = WindowsError 665 except NameError: 666 WinError = PlainWindowsError 667 668 669 if can_read_reg: 670 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT 671 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE 672 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER 673 HKEY_USERS = hkey_mod.HKEY_USERS 674
675 - def RegGetValue(root, key):
676 """This utility function returns a value in the registry 677 without having to open the key first. Only available on 678 Windows platforms with a version of Python that can read the 679 registry. Returns the same thing as 680 SCons.Util.RegQueryValueEx, except you just specify the entire 681 path to the value, and don't have to bother opening the key 682 first. So: 683 684 Instead of: 685 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, 686 r'SOFTWARE\Microsoft\Windows\CurrentVersion') 687 out = SCons.Util.RegQueryValueEx(k, 688 'ProgramFilesDir') 689 690 You can write: 691 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, 692 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir') 693 """ 694 # I would use os.path.split here, but it's not a filesystem 695 # path... 696 p = key.rfind('\\') + 1 697 keyp = key[:p-1] # -1 to omit trailing slash 698 val = key[p:] 699 k = RegOpenKeyEx(root, keyp) 700 return RegQueryValueEx(k,val)
701 else: 702 HKEY_CLASSES_ROOT = None 703 HKEY_LOCAL_MACHINE = None 704 HKEY_CURRENT_USER = None 705 HKEY_USERS = None 706
707 - def RegGetValue(root, key):
708 raise WinError
709
710 - def RegOpenKeyEx(root, key):
711 raise WinError
712 713 if sys.platform == 'win32': 714
715 - def WhereIs(file, path=None, pathext=None, reject=[]):
716 if path is None: 717 try: 718 path = os.environ['PATH'] 719 except KeyError: 720 return None 721 if is_String(path): 722 path = path.split(os.pathsep) 723 if pathext is None: 724 try: 725 pathext = os.environ['PATHEXT'] 726 except KeyError: 727 pathext = '.COM;.EXE;.BAT;.CMD' 728 if is_String(pathext): 729 pathext = pathext.split(os.pathsep) 730 for ext in pathext: 731 if ext.lower() == file[-len(ext):].lower(): 732 pathext = [''] 733 break 734 if not is_List(reject) and not is_Tuple(reject): 735 reject = [reject] 736 for dir in path: 737 f = os.path.join(dir, file) 738 for ext in pathext: 739 fext = f + ext 740 if os.path.isfile(fext): 741 try: 742 reject.index(fext) 743 except ValueError: 744 return os.path.normpath(fext) 745 continue 746 return None
747 748 elif os.name == 'os2': 749
750 - def WhereIs(file, path=None, pathext=None, reject=[]):
751 if path is None: 752 try: 753 path = os.environ['PATH'] 754 except KeyError: 755 return None 756 if is_String(path): 757 path = path.split(os.pathsep) 758 if pathext is None: 759 pathext = ['.exe', '.cmd'] 760 for ext in pathext: 761 if ext.lower() == file[-len(ext):].lower(): 762 pathext = [''] 763 break 764 if not is_List(reject) and not is_Tuple(reject): 765 reject = [reject] 766 for dir in path: 767 f = os.path.join(dir, file) 768 for ext in pathext: 769 fext = f + ext 770 if os.path.isfile(fext): 771 try: 772 reject.index(fext) 773 except ValueError: 774 return os.path.normpath(fext) 775 continue 776 return None
777 778 else: 779
780 - def WhereIs(file, path=None, pathext=None, reject=[]):
781 import stat 782 if path is None: 783 try: 784 path = os.environ['PATH'] 785 except KeyError: 786 return None 787 if is_String(path): 788 path = path.split(os.pathsep) 789 if not is_List(reject) and not is_Tuple(reject): 790 reject = [reject] 791 for d in path: 792 f = os.path.join(d, file) 793 if os.path.isfile(f): 794 try: 795 st = os.stat(f) 796 except OSError: 797 # os.stat() raises OSError, not IOError if the file 798 # doesn't exist, so in this case we let IOError get 799 # raised so as to not mask possibly serious disk or 800 # network issues. 801 continue 802 if stat.S_IMODE(st[stat.ST_MODE]) & 0o111: 803 try: 804 reject.index(f) 805 except ValueError: 806 return os.path.normpath(f) 807 continue 808 return None
809
810 -def PrependPath(oldpath, newpath, sep = os.pathsep, 811 delete_existing=1, canonicalize=None):
812 """This prepends newpath elements to the given oldpath. Will only 813 add any particular path once (leaving the first one it encounters 814 and ignoring the rest, to preserve path order), and will 815 os.path.normpath and os.path.normcase all paths to help assure 816 this. This can also handle the case where the given old path 817 variable is a list instead of a string, in which case a list will 818 be returned instead of a string. 819 820 Example: 821 Old Path: "/foo/bar:/foo" 822 New Path: "/biz/boom:/foo" 823 Result: "/biz/boom:/foo:/foo/bar" 824 825 If delete_existing is 0, then adding a path that exists will 826 not move it to the beginning; it will stay where it is in the 827 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 # First uniquify the old paths, making sure to 852 # preserve the first instance (in Unix/Linux, 853 # the first one wins), and remembering them in normpaths. 854 # Then insert the new paths at the head of the list 855 # if they're not already in the normpaths list. 856 result = [] 857 normpaths = [] 858 for path in paths: 859 if not path: 860 continue 861 normpath = os.path.normpath(os.path.normcase(path)) 862 if normpath not in normpaths: 863 result.append(path) 864 normpaths.append(normpath) 865 newpaths.reverse() # since we're inserting at the head 866 for path in newpaths: 867 if not path: 868 continue 869 normpath = os.path.normpath(os.path.normcase(path)) 870 if normpath not in normpaths: 871 result.insert(0, path) 872 normpaths.append(normpath) 873 paths = result 874 875 else: 876 newpaths = newpaths + paths # prepend new paths 877 878 normpaths = [] 879 paths = [] 880 # now we add them only if they are unique 881 for path in newpaths: 882 normpath = os.path.normpath(os.path.normcase(path)) 883 if path and not normpath in normpaths: 884 paths.append(path) 885 normpaths.append(normpath) 886 887 if is_list: 888 return paths 889 else: 890 return sep.join(paths)
891
892 -def AppendPath(oldpath, newpath, sep = os.pathsep, 893 delete_existing=1, canonicalize=None):
894 """This appends new path elements to the given old path. Will 895 only add any particular path once (leaving the last one it 896 encounters and ignoring the rest, to preserve path order), and 897 will os.path.normpath and os.path.normcase all paths to help 898 assure this. This can also handle the case where the given old 899 path variable is a list instead of a string, in which case a list 900 will be returned instead of a string. 901 902 Example: 903 Old Path: "/foo/bar:/foo" 904 New Path: "/biz/boom:/foo" 905 Result: "/foo/bar:/biz/boom:/foo" 906 907 If delete_existing is 0, then adding a path that exists 908 will not move it to the end; it will stay where it is in the list. 909 910 If canonicalize is not None, it is applied to each element of 911 newpath before use. 912 """ 913 914 orig = oldpath 915 is_list = 1 916 paths = orig 917 if not is_List(orig) and not is_Tuple(orig): 918 paths = paths.split(sep) 919 is_list = 0 920 921 if is_String(newpath): 922 newpaths = newpath.split(sep) 923 elif not is_List(newpath) and not is_Tuple(newpath): 924 newpaths = [ newpath ] # might be a Dir 925 else: 926 newpaths = newpath 927 928 if canonicalize: 929 newpaths=list(map(canonicalize, newpaths)) 930 931 if not delete_existing: 932 # add old paths to result, then 933 # add new paths if not already present 934 # (I thought about using a dict for normpaths for speed, 935 # but it's not clear hashing the strings would be faster 936 # than linear searching these typically short lists.) 937 result = [] 938 normpaths = [] 939 for path in paths: 940 if not path: 941 continue 942 result.append(path) 943 normpaths.append(os.path.normpath(os.path.normcase(path))) 944 for path in newpaths: 945 if not path: 946 continue 947 normpath = os.path.normpath(os.path.normcase(path)) 948 if normpath not in normpaths: 949 result.append(path) 950 normpaths.append(normpath) 951 paths = result 952 else: 953 # start w/ new paths, add old ones if not present, 954 # then reverse. 955 newpaths = paths + newpaths # append new paths 956 newpaths.reverse() 957 958 normpaths = [] 959 paths = [] 960 # now we add them only if they are unique 961 for path in newpaths: 962 normpath = os.path.normpath(os.path.normcase(path)) 963 if path and not normpath in normpaths: 964 paths.append(path) 965 normpaths.append(normpath) 966 paths.reverse() 967 968 if is_list: 969 return paths 970 else: 971 return sep.join(paths)
972
973 -def AddPathIfNotExists(env_dict, key, path, sep=os.pathsep):
974 """This function will take 'key' out of the dictionary 975 'env_dict', then add the path 'path' to that key if it is not 976 already there. This treats the value of env_dict[key] as if it 977 has a similar format to the PATH variable...a list of paths 978 separated by tokens. The 'path' will get added to the list if it 979 is not already there.""" 980 try: 981 is_list = 1 982 paths = env_dict[key] 983 if not is_List(env_dict[key]): 984 paths = paths.split(sep) 985 is_list = 0 986 if os.path.normcase(path) not in list(map(os.path.normcase, paths)): 987 paths = [ path ] + paths 988 if is_list: 989 env_dict[key] = paths 990 else: 991 env_dict[key] = sep.join(paths) 992 except KeyError: 993 env_dict[key] = path
994 995 if sys.platform == 'cygwin':
996 - def get_native_path(path):
997 """Transforms an absolute path into a native path for the system. In 998 Cygwin, this converts from a Cygwin path to a Windows one.""" 999 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1000 else:
1001 - def get_native_path(path):
1002 """Transforms an absolute path into a native path for the system. 1003 Non-Cygwin version, just leave the path alone.""" 1004 return path
1005 1006 display = DisplayEngine() 1007
1008 -def Split(arg):
1009 if is_List(arg) or is_Tuple(arg): 1010 return arg 1011 elif is_String(arg): 1012 return arg.split() 1013 else: 1014 return [arg]
1015
1016 -class CLVar(UserList):
1017 """A class for command-line construction variables. 1018 1019 This is a list that uses Split() to split an initial string along 1020 white-space arguments, and similarly to split any strings that get 1021 added. This allows us to Do the Right Thing with Append() and 1022 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1 1023 arg2') regardless of whether a user adds a list or a string to a 1024 command-line construction variable. 1025 """
1026 - def __init__(self, seq = []):
1027 UserList.__init__(self, Split(seq))
1028 - def __add__(self, other):
1029 return UserList.__add__(self, CLVar(other))
1030 - def __radd__(self, other):
1031 return UserList.__radd__(self, CLVar(other))
1032 - def __coerce__(self, other):
1033 return (self, CLVar(other))
1034 - def __str__(self):
1035 return ' '.join(self.data)
1036 1037 # A dictionary that preserves the order in which items are added. 1038 # Submitted by David Benjamin to ActiveState's Python Cookbook web site: 1039 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 1040 # Including fixes/enhancements from the follow-on discussions.
1041 -class OrderedDict(UserDict):
1042 - def __init__(self, dict = None):
1043 self._keys = [] 1044 UserDict.__init__(self, dict)
1045
1046 - def __delitem__(self, key):
1047 UserDict.__delitem__(self, key) 1048 self._keys.remove(key)
1049
1050 - def __setitem__(self, key, item):
1051 UserDict.__setitem__(self, key, item) 1052 if key not in self._keys: self._keys.append(key)
1053
1054 - def clear(self):
1055 UserDict.clear(self) 1056 self._keys = []
1057
1058 - def copy(self):
1059 dict = OrderedDict() 1060 dict.update(self) 1061 return dict
1062
1063 - def items(self):
1064 return list(zip(self._keys, list(self.values())))
1065
1066 - def keys(self):
1067 return self._keys[:]
1068
1069 - def popitem(self):
1070 try: 1071 key = self._keys[-1] 1072 except IndexError: 1073 raise KeyError('dictionary is empty') 1074 1075 val = self[key] 1076 del self[key] 1077 1078 return (key, val)
1079
1080 - def setdefault(self, key, failobj = None):
1081 UserDict.setdefault(self, key, failobj) 1082 if key not in self._keys: self._keys.append(key)
1083
1084 - def update(self, dict):
1085 for (key, val) in dict.items(): 1086 self.__setitem__(key, val)
1087
1088 - def values(self):
1089 return list(map(self.get, self._keys))
1090
1091 -class Selector(OrderedDict):
1092 """A callable ordered dictionary that maps file suffixes to 1093 dictionary values. We preserve the order in which items are added 1094 so that get_suffix() calls always return the first suffix added."""
1095 - def __call__(self, env, source, ext=None):
1096 if ext is None: 1097 try: 1098 ext = source[0].get_suffix() 1099 except IndexError: 1100 ext = "" 1101 try: 1102 return self[ext] 1103 except KeyError: 1104 # Try to perform Environment substitution on the keys of 1105 # the dictionary before giving up. 1106 s_dict = {} 1107 for (k,v) in self.items(): 1108 if k is not None: 1109 s_k = env.subst(k) 1110 if s_k in s_dict: 1111 # We only raise an error when variables point 1112 # to the same suffix. If one suffix is literal 1113 # and a variable suffix contains this literal, 1114 # the literal wins and we don't raise an error. 1115 raise KeyError(s_dict[s_k][0], k, s_k) 1116 s_dict[s_k] = (k,v) 1117 try: 1118 return s_dict[ext][1] 1119 except KeyError: 1120 try: 1121 return self[None] 1122 except KeyError: 1123 return None
1124 1125 1126 if sys.platform == 'cygwin': 1127 # On Cygwin, os.path.normcase() lies, so just report back the 1128 # fact that the underlying Windows OS is case-insensitive.
1129 - def case_sensitive_suffixes(s1, s2):
1130 return 0
1131 else:
1132 - def case_sensitive_suffixes(s1, s2):
1133 return (os.path.normcase(s1) != os.path.normcase(s2))
1134
1135 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1136 if pre: 1137 path, fn = os.path.split(os.path.normpath(fname)) 1138 if fn[:len(pre)] != pre: 1139 fname = os.path.join(path, pre + fn) 1140 # Only append a suffix if the suffix we're going to add isn't already 1141 # there, and if either we've been asked to ensure the specific suffix 1142 # is present or there's no suffix on it at all. 1143 if suf and fname[-len(suf):] != suf and \ 1144 (ensure_suffix or not splitext(fname)[1]): 1145 fname = fname + suf 1146 return fname
1147 1148 1149 1150 # From Tim Peters, 1151 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1152 # ASPN: Python Cookbook: Remove duplicates from a sequence 1153 # (Also in the printed Python Cookbook.) 1154
1155 -def unique(s):
1156 """Return a list of the elements in s, but without duplicates. 1157 1158 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 1159 unique("abcabc") some permutation of ["a", "b", "c"], and 1160 unique(([1, 2], [2, 3], [1, 2])) some permutation of 1161 [[2, 3], [1, 2]]. 1162 1163 For best speed, all sequence elements should be hashable. Then 1164 unique() will usually work in linear time. 1165 1166 If not possible, the sequence elements should enjoy a total 1167 ordering, and if list(s).sort() doesn't raise TypeError it's 1168 assumed that they do enjoy a total ordering. Then unique() will 1169 usually work in O(N*log2(N)) time. 1170 1171 If that's not possible either, the sequence elements must support 1172 equality-testing. Then unique() will usually work in quadratic 1173 time. 1174 """ 1175 1176 n = len(s) 1177 if n == 0: 1178 return [] 1179 1180 # Try using a dict first, as that's the fastest and will usually 1181 # work. If it doesn't work, it will usually fail quickly, so it 1182 # usually doesn't cost much to *try* it. It requires that all the 1183 # sequence elements be hashable, and support equality comparison. 1184 u = {} 1185 try: 1186 for x in s: 1187 u[x] = 1 1188 except TypeError: 1189 pass # move on to the next method 1190 else: 1191 return list(u.keys()) 1192 del u 1193 1194 # We can't hash all the elements. Second fastest is to sort, 1195 # which brings the equal elements together; then duplicates are 1196 # easy to weed out in a single pass. 1197 # NOTE: Python's list.sort() was designed to be efficient in the 1198 # presence of many duplicate elements. This isn't true of all 1199 # sort functions in all languages or libraries, so this approach 1200 # is more effective in Python than it may be elsewhere. 1201 try: 1202 t = sorted(s) 1203 except TypeError: 1204 pass # move on to the next method 1205 else: 1206 assert n > 0 1207 last = t[0] 1208 lasti = i = 1 1209 while i < n: 1210 if t[i] != last: 1211 t[lasti] = last = t[i] 1212 lasti = lasti + 1 1213 i = i + 1 1214 return t[:lasti] 1215 del t 1216 1217 # Brute force is all that's left. 1218 u = [] 1219 for x in s: 1220 if x not in u: 1221 u.append(x) 1222 return u
1223 1224 1225 1226 # From Alex Martelli, 1227 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1228 # ASPN: Python Cookbook: Remove duplicates from a sequence 1229 # First comment, dated 2001/10/13. 1230 # (Also in the printed Python Cookbook.) 1231
1232 -def uniquer(seq, idfun=None):
1233 if idfun is None: 1234 def idfun(x): return x 1235 seen = {} 1236 result = [] 1237 for item in seq: 1238 marker = idfun(item) 1239 # in old Python versions: 1240 # if seen.has_key(marker) 1241 # but in new ones: 1242 if marker in seen: continue 1243 seen[marker] = 1 1244 result.append(item) 1245 return result
1246 1247 # A more efficient implementation of Alex's uniquer(), this avoids the 1248 # idfun() argument and function-call overhead by assuming that all 1249 # items in the sequence are hashable. 1250
1251 -def uniquer_hashables(seq):
1252 seen = {} 1253 result = [] 1254 for item in seq: 1255 #if not item in seen: 1256 if item not in seen: 1257 seen[item] = 1 1258 result.append(item) 1259 return result
1260 1261 1262 # Recipe 19.11 "Reading Lines with Continuation Characters", 1263 # by Alex Martelli, straight from the Python CookBook (2nd edition).
1264 -def logical_lines(physical_lines, joiner=''.join):
1265 logical_line = [] 1266 for line in physical_lines: 1267 stripped = line.rstrip() 1268 if stripped.endswith('\\'): 1269 # a line which continues w/the next physical line 1270 logical_line.append(stripped[:-1]) 1271 else: 1272 # a line which does not continue, end of logical line 1273 logical_line.append(line) 1274 yield joiner(logical_line) 1275 logical_line = [] 1276 if logical_line: 1277 # end of sequence implies end of last logical line 1278 yield joiner(logical_line)
1279 1280
1281 -class LogicalLines(object):
1282 """ Wrapper class for the logical_lines method. 1283 1284 Allows us to read all "logical" lines at once from a 1285 given file object. 1286 """ 1287
1288 - def __init__(self, fileobj):
1289 self.fileobj = fileobj
1290
1291 - def readlines(self):
1292 result = [l for l in logical_lines(self.fileobj)] 1293 return result
1294 1295
1296 -class UniqueList(UserList):
1297 - def __init__(self, seq = []):
1298 UserList.__init__(self, seq) 1299 self.unique = True
1300 - def __make_unique(self):
1301 if not self.unique: 1302 self.data = uniquer_hashables(self.data) 1303 self.unique = True
1304 - def __lt__(self, other):
1305 self.__make_unique() 1306 return UserList.__lt__(self, other)
1307 - def __le__(self, other):
1308 self.__make_unique() 1309 return UserList.__le__(self, other)
1310 - def __eq__(self, other):
1311 self.__make_unique() 1312 return UserList.__eq__(self, other)
1313 - def __ne__(self, other):
1314 self.__make_unique() 1315 return UserList.__ne__(self, other)
1316 - def __gt__(self, other):
1317 self.__make_unique() 1318 return UserList.__gt__(self, other)
1319 - def __ge__(self, other):
1320 self.__make_unique() 1321 return UserList.__ge__(self, other)
1322 - def __cmp__(self, other):
1323 self.__make_unique() 1324 return UserList.__cmp__(self, other)
1325 - def __len__(self):
1326 self.__make_unique() 1327 return UserList.__len__(self)
1328 - def __getitem__(self, i):
1329 self.__make_unique() 1330 return UserList.__getitem__(self, i)
1331 - def __setitem__(self, i, item):
1332 UserList.__setitem__(self, i, item) 1333 self.unique = False
1334 - def __getslice__(self, i, j):
1335 self.__make_unique() 1336 return UserList.__getslice__(self, i, j)
1337 - def __setslice__(self, i, j, other):
1338 UserList.__setslice__(self, i, j, other) 1339 self.unique = False
1340 - def __add__(self, other):
1341 result = UserList.__add__(self, other) 1342 result.unique = False 1343 return result
1344 - def __radd__(self, other):
1345 result = UserList.__radd__(self, other) 1346 result.unique = False 1347 return result
1348 - def __iadd__(self, other):
1349 result = UserList.__iadd__(self, other) 1350 result.unique = False 1351 return result
1352 - def __mul__(self, other):
1353 result = UserList.__mul__(self, other) 1354 result.unique = False 1355 return result
1356 - def __rmul__(self, other):
1357 result = UserList.__rmul__(self, other) 1358 result.unique = False 1359 return result
1360 - def __imul__(self, other):
1361 result = UserList.__imul__(self, other) 1362 result.unique = False 1363 return result
1364 - def append(self, item):
1365 UserList.append(self, item) 1366 self.unique = False
1367 - def insert(self, i):
1368 UserList.insert(self, i) 1369 self.unique = False
1370 - def count(self, item):
1371 self.__make_unique() 1372 return UserList.count(self, item)
1373 - def index(self, item):
1374 self.__make_unique() 1375 return UserList.index(self, item)
1376 - def reverse(self):
1377 self.__make_unique() 1378 UserList.reverse(self)
1379 - def sort(self, *args, **kwds):
1380 self.__make_unique() 1381 return UserList.sort(self, *args, **kwds)
1382 - def extend(self, other):
1383 UserList.extend(self, other) 1384 self.unique = False
1385 1386
1387 -class Unbuffered(object):
1388 """ 1389 A proxy class that wraps a file object, flushing after every write, 1390 and delegating everything else to the wrapped object. 1391 """
1392 - def __init__(self, file):
1393 self.file = file 1394 self.softspace = 0 ## backward compatibility; not supported in Py3k
1395 - def write(self, arg):
1396 try: 1397 self.file.write(arg) 1398 self.file.flush() 1399 except IOError: 1400 # Stdout might be connected to a pipe that has been closed 1401 # by now. The most likely reason for the pipe being closed 1402 # is that the user has press ctrl-c. It this is the case, 1403 # then SCons is currently shutdown. We therefore ignore 1404 # IOError's here so that SCons can continue and shutdown 1405 # properly so that the .sconsign is correctly written 1406 # before SCons exits. 1407 pass
1408 - def __getattr__(self, attr):
1409 return getattr(self.file, attr)
1410
1411 -def make_path_relative(path):
1412 """ makes an absolute path name to a relative pathname. 1413 """ 1414 if os.path.isabs(path): 1415 drive_s,path = os.path.splitdrive(path) 1416 1417 import re 1418 if not drive_s: 1419 path=re.compile("/*(.*)").findall(path)[0] 1420 else: 1421 path=path[1:] 1422 1423 assert( not os.path.isabs( path ) ), path 1424 return path
1425 1426 1427 1428 # The original idea for AddMethod() and RenameFunction() come from the 1429 # following post to the ActiveState Python Cookbook: 1430 # 1431 # ASPN: Python Cookbook : Install bound methods in an instance 1432 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 1433 # 1434 # That code was a little fragile, though, so the following changes 1435 # have been wrung on it: 1436 # 1437 # * Switched the installmethod() "object" and "function" arguments, 1438 # so the order reflects that the left-hand side is the thing being 1439 # "assigned to" and the right-hand side is the value being assigned. 1440 # 1441 # * Changed explicit type-checking to the "try: klass = object.__class__" 1442 # block in installmethod() below so that it still works with the 1443 # old-style classes that SCons uses. 1444 # 1445 # * Replaced the by-hand creation of methods and functions with use of 1446 # the "new" module, as alluded to in Alex Martelli's response to the 1447 # following Cookbook post: 1448 # 1449 # ASPN: Python Cookbook : Dynamically added methods to a class 1450 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 1451
1452 -def AddMethod(obj, function, name=None):
1453 """ 1454 Adds either a bound method to an instance or the function itself (or an unbound method in Python 2) to a class. 1455 If name is ommited the name of the specified function 1456 is used by default. 1457 1458 Example:: 1459 1460 a = A() 1461 def f(self, x, y): 1462 self.z = x + y 1463 AddMethod(f, A, "add") 1464 a.add(2, 4) 1465 print(a.z) 1466 AddMethod(lambda self, i: self.l[i], a, "listIndex") 1467 print(a.listIndex(5)) 1468 """ 1469 if name is None: 1470 name = function.__name__ 1471 else: 1472 function = RenameFunction(function, name) 1473 1474 # Note the Python version checks - WLB 1475 # Python 3.3 dropped the 3rd parameter from types.MethodType 1476 if hasattr(obj, '__class__') and obj.__class__ is not type: 1477 # "obj" is an instance, so it gets a bound method. 1478 if sys.version_info[:2] > (3, 2): 1479 method = MethodType(function, obj) 1480 else: 1481 method = MethodType(function, obj, obj.__class__) 1482 else: 1483 # Handle classes 1484 method = function 1485 1486 setattr(obj, name, method)
1487
1488 -def RenameFunction(function, name):
1489 """ 1490 Returns a function identical to the specified function, but with 1491 the specified name. 1492 """ 1493 return FunctionType(function.__code__, 1494 function.__globals__, 1495 name, 1496 function.__defaults__)
1497 1498 1499 md5 = False 1500 1501
1502 -def MD5signature(s):
1503 return str(s)
1504 1505
1506 -def MD5filesignature(fname, chunksize=65536):
1507 with open(fname, "rb") as f: 1508 result = f.read() 1509 return result
1510 1511 try: 1512 import hashlib 1513 except ImportError: 1514 pass 1515 else: 1516 if hasattr(hashlib, 'md5'): 1517 md5 = True 1518
1519 - def MD5signature(s):
1520 m = hashlib.md5() 1521 1522 try: 1523 m.update(to_bytes(s)) 1524 except TypeError as e: 1525 m.update(to_bytes(str(s))) 1526 1527 return m.hexdigest()
1528
1529 - def MD5filesignature(fname, chunksize=65536):
1530 m = hashlib.md5() 1531 f = open(fname, "rb") 1532 while True: 1533 blck = f.read(chunksize) 1534 if not blck: 1535 break 1536 m.update(to_bytes(blck)) 1537 f.close() 1538 return m.hexdigest()
1539
1540 -def MD5collect(signatures):
1541 """ 1542 Collects a list of signatures into an aggregate signature. 1543 1544 signatures - a list of signatures 1545 returns - the aggregate signature 1546 """ 1547 if len(signatures) == 1: 1548 return signatures[0] 1549 else: 1550 return MD5signature(', '.join(signatures))
1551 1552 1553
1554 -def silent_intern(x):
1555 """ 1556 Perform sys.intern() on the passed argument and return the result. 1557 If the input is ineligible (e.g. a unicode string) the original argument is 1558 returned and no exception is thrown. 1559 """ 1560 try: 1561 return sys.intern(x) 1562 except TypeError: 1563 return x
1564 1565 1566 1567 # From Dinu C. Gherman, 1568 # Python Cookbook, second edition, recipe 6.17, p. 277. 1569 # Also: 1570 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 1571 # ASPN: Python Cookbook: Null Object Design Pattern 1572 1573 #TODO??? class Null(object):
1574 -class Null(object):
1575 """ Null objects always and reliably "do nothing." """
1576 - def __new__(cls, *args, **kwargs):
1577 if not '_instance' in vars(cls): 1578 cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) 1579 return cls._instance
1580 - def __init__(self, *args, **kwargs):
1581 pass
1582 - def __call__(self, *args, **kwargs):
1583 return self
1584 - def __repr__(self):
1585 return "Null(0x%08X)" % id(self)
1586 - def __nonzero__(self):
1587 return False
1588 - def __bool__(self):
1589 return False
1590 - def __getattr__(self, name):
1591 return self
1592 - def __setattr__(self, name, value):
1593 return self
1594 - def __delattr__(self, name):
1595 return self
1596
1597 -class NullSeq(Null):
1598 - def __len__(self):
1599 return 0
1600 - def __iter__(self):
1601 return iter(())
1602 - def __getitem__(self, i):
1603 return self
1604 - def __delitem__(self, i):
1605 return self
1606 - def __setitem__(self, i, v):
1607 return self
1608 1609 1610 del __revision__ 1611
1612 -def to_bytes (s):
1613 if isinstance (s, (bytes, bytearray)) or bytes is str: 1614 return s 1615 return bytes (s, 'utf-8')
1616
1617 -def to_str (s):
1618 if bytes is str or is_String(s): 1619 return s 1620 return str (s, 'utf-8')
1621 1622 1623 1624 # No cmp in py3, so we'll define it.
1625 -def cmp(a, b):
1626 """ 1627 Define cmp because it's no longer available in python3 1628 Works under python 2 as well 1629 """ 1630 return (a > b) - (a < b)
1631 1632 1633 # Local Variables: 1634 # tab-width:4 1635 # indent-tabs-mode:nil 1636 # End: 1637 # vim: set expandtab tabstop=4 shiftwidth=4: 1638