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 - 2019 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 3a41ed6b288cee8d085373ad7fa02894e1903864 2019-01-23 17:30:35 bdeegan" 
  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 collections import UserDict, UserList, UserString 
  41  except ImportError: 
  42      from UserDict import UserDict 
  43      from UserList import UserList 
  44      from UserString import UserString 
  45   
  46  try: 
  47      from collections.abc import Iterable, MappingView 
  48  except ImportError: 
  49      from collections import Iterable 
  50   
  51  from collections import OrderedDict 
  52   
  53  # Don't "from types import ..." these because we need to get at the 
  54  # types module later to look for UnicodeType. 
  55   
  56  # Below not used? 
  57  # InstanceType    = types.InstanceType 
  58   
  59  MethodType      = types.MethodType 
  60  FunctionType    = types.FunctionType 
  61   
  62  try: 
  63      _ = type(unicode) 
  64  except NameError: 
  65      UnicodeType = str 
  66  else: 
  67      UnicodeType = unicode 
  68   
69 -def dictify(keys, values, result={}):
70 for k, v in zip(keys, values): 71 result[k] = v 72 return result
73 74 _altsep = os.altsep 75 if _altsep is None and sys.platform == 'win32': 76 # My ActivePython 2.0.1 doesn't set os.altsep! What gives? 77 _altsep = '/' 78 if _altsep:
79 - def rightmost_separator(path, sep):
80 return max(path.rfind(sep), path.rfind(_altsep))
81 else:
82 - def rightmost_separator(path, sep):
83 return path.rfind(sep)
84 85 # First two from the Python Cookbook, just for completeness. 86 # (Yeah, yeah, YAGNI...)
87 -def containsAny(str, set):
88 """Check whether sequence str contains ANY of the items in set.""" 89 for c in set: 90 if c in str: return 1 91 return 0
92
93 -def containsAll(str, set):
94 """Check whether sequence str contains ALL of the items in set.""" 95 for c in set: 96 if c not in str: return 0 97 return 1
98
99 -def containsOnly(str, set):
100 """Check whether sequence str contains ONLY items in set.""" 101 for c in str: 102 if c not in set: return 0 103 return 1
104
105 -def splitext(path):
106 """Same as os.path.splitext() but faster.""" 107 sep = rightmost_separator(path, os.sep) 108 dot = path.rfind('.') 109 # An ext is only real if it has at least one non-digit char 110 if dot > sep and not containsOnly(path[dot:], "0123456789."): 111 return path[:dot],path[dot:] 112 else: 113 return path,""
114
115 -def updrive(path):
116 """ 117 Make the drive letter (if any) upper case. 118 This is useful because Windows is inconsistent on the case 119 of the drive letter, which can cause inconsistencies when 120 calculating command signatures. 121 """ 122 drive, rest = os.path.splitdrive(path) 123 if drive: 124 path = drive.upper() + rest 125 return path
126
127 -class NodeList(UserList):
128 """This class is almost exactly like a regular list of Nodes 129 (actually it can hold any object), with one important difference. 130 If you try to get an attribute from this list, it will return that 131 attribute from every item in the list. For example: 132 133 >>> someList = NodeList([ ' foo ', ' bar ' ]) 134 >>> someList.strip() 135 [ 'foo', 'bar' ] 136 """ 137 138 # def __init__(self, initlist=None): 139 # self.data = [] 140 # # print("TYPE:%s"%type(initlist)) 141 # if initlist is not None: 142 # # XXX should this accept an arbitrary sequence? 143 # if type(initlist) == type(self.data): 144 # self.data[:] = initlist 145 # elif isinstance(initlist, (UserList, NodeList)): 146 # self.data[:] = initlist.data[:] 147 # elif isinstance(initlist, Iterable): 148 # self.data = list(initlist) 149 # else: 150 # self.data = [ initlist,] 151 152
153 - def __nonzero__(self):
154 return len(self.data) != 0
155
156 - def __bool__(self):
157 return self.__nonzero__()
158
159 - def __str__(self):
160 return ' '.join(map(str, self.data))
161
162 - def __iter__(self):
163 return iter(self.data)
164
165 - def __call__(self, *args, **kwargs):
166 result = [x(*args, **kwargs) for x in self.data] 167 return self.__class__(result)
168
169 - def __getattr__(self, name):
170 result = [getattr(x, name) for x in self.data] 171 return self.__class__(result)
172
173 - def __getitem__(self, index):
174 """ 175 This comes for free on py2, 176 but py3 slices of NodeList are returning a list 177 breaking slicing nodelist and refering to 178 properties and methods on contained object 179 """ 180 # return self.__class__(self.data[index]) 181 182 if isinstance(index, slice): 183 # Expand the slice object using range() 184 # limited by number of items in self.data 185 indices = index.indices(len(self.data)) 186 return self.__class__([self[x] for x in 187 range(*indices)]) 188 else: 189 # Return one item of the tart 190 return self.data[index]
191 192 193 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$') 194
195 -def get_environment_var(varstr):
196 """Given a string, first determine if it looks like a reference 197 to a single environment variable, like "$FOO" or "${FOO}". 198 If so, return that variable with no decorations ("FOO"). 199 If not, return None.""" 200 mo=_get_env_var.match(to_String(varstr)) 201 if mo: 202 var = mo.group(1) 203 if var[0] == '{': 204 return var[1:-1] 205 else: 206 return var 207 else: 208 return None
209
210 -class DisplayEngine(object):
211 print_it = True
212 - def __call__(self, text, append_newline=1):
213 if not self.print_it: 214 return 215 if append_newline: text = text + '\n' 216 try: 217 sys.stdout.write(UnicodeType(text)) 218 except IOError: 219 # Stdout might be connected to a pipe that has been closed 220 # by now. The most likely reason for the pipe being closed 221 # is that the user has press ctrl-c. It this is the case, 222 # then SCons is currently shutdown. We therefore ignore 223 # IOError's here so that SCons can continue and shutdown 224 # properly so that the .sconsign is correctly written 225 # before SCons exits. 226 pass
227
228 - def set_mode(self, mode):
229 self.print_it = mode
230 231
232 -def render_tree(root, child_func, prune=0, margin=[0], visited=None):
233 """ 234 Render a tree of nodes into an ASCII tree view. 235 236 :Parameters: 237 - `root`: the root node of the tree 238 - `child_func`: the function called to get the children of a node 239 - `prune`: don't visit the same node twice 240 - `margin`: the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe. 241 - `visited`: a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune. 242 """ 243 244 rname = str(root) 245 246 # Initialize 'visited' dict, if required 247 if visited is None: 248 visited = {} 249 250 children = child_func(root) 251 retval = "" 252 for pipe in margin[:-1]: 253 if pipe: 254 retval = retval + "| " 255 else: 256 retval = retval + " " 257 258 if rname in visited: 259 return retval + "+-[" + rname + "]\n" 260 261 retval = retval + "+-" + rname + "\n" 262 if not prune: 263 visited = copy.copy(visited) 264 visited[rname] = 1 265 266 for i in range(len(children)): 267 margin.append(i < len(children)-1) 268 retval = retval + render_tree(children[i], child_func, prune, margin, visited) 269 margin.pop() 270 271 return retval
272 273 IDX = lambda N: N and 1 or 0 274 275 332 margins = list(map(MMM, margin[:-1])) 333 334 children = child_func(root) 335 336 if prune and rname in visited and children: 337 sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + '\n') 338 return 339 340 sys.stdout.write(''.join(tags + margins + ['+-', rname]) + '\n') 341 342 visited[rname] = 1 343 344 if children: 345 margin.append(1) 346 idx = IDX(showtags) 347 for C in children[:-1]: 348 print_tree(C, child_func, prune, idx, margin, visited) 349 margin[-1] = 0 350 print_tree(children[-1], child_func, prune, idx, margin, visited) 351 margin.pop() 352 353 354 355 # Functions for deciding if things are like various types, mainly to 356 # handle UserDict, UserList and UserString like their underlying types. 357 # 358 # Yes, all of this manual testing breaks polymorphism, and the real 359 # Pythonic way to do all of this would be to just try it and handle the 360 # exception, but handling the exception when it's not the right type is 361 # often too slow. 362 363 # We are using the following trick to speed up these 364 # functions. Default arguments are used to take a snapshot of 365 # the global functions and constants used by these functions. This 366 # transforms accesses to global variable into local variables 367 # accesses (i.e. LOAD_FAST instead of LOAD_GLOBAL). 368 369 DictTypes = (dict, UserDict) 370 ListTypes = (list, UserList) 371 372 try: 373 # Handle getting dictionary views. 374 SequenceTypes = (list, tuple, UserList, MappingView) 375 except NameError: 376 SequenceTypes = (list, tuple, UserList) 377 378 379 # Note that profiling data shows a speed-up when comparing 380 # explicitly with str and unicode instead of simply comparing 381 # with basestring. (at least on Python 2.5.1) 382 try: 383 StringTypes = (str, unicode, UserString) 384 except NameError: 385 StringTypes = (str, UserString) 386 387 # Empirically, it is faster to check explicitly for str and 388 # unicode than for basestring. 389 try: 390 BaseStringTypes = (str, unicode) 391 except NameError: 392 BaseStringTypes = (str) 393
394 -def is_Dict(obj, isinstance=isinstance, DictTypes=DictTypes):
395 return isinstance(obj, DictTypes)
396
397 -def is_List(obj, isinstance=isinstance, ListTypes=ListTypes):
398 return isinstance(obj, ListTypes)
399
400 -def is_Sequence(obj, isinstance=isinstance, SequenceTypes=SequenceTypes):
401 return isinstance(obj, SequenceTypes)
402
403 -def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
404 return isinstance(obj, tuple)
405
406 -def is_String(obj, isinstance=isinstance, StringTypes=StringTypes):
407 return isinstance(obj, StringTypes)
408
409 -def is_Scalar(obj, isinstance=isinstance, StringTypes=StringTypes, SequenceTypes=SequenceTypes):
410 # Profiling shows that there is an impressive speed-up of 2x 411 # when explicitly checking for strings instead of just not 412 # sequence when the argument (i.e. obj) is already a string. 413 # But, if obj is a not string then it is twice as fast to 414 # check only for 'not sequence'. The following code therefore 415 # assumes that the obj argument is a string most of the time. 416 return isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes)
417
418 -def do_flatten(sequence, result, isinstance=isinstance, 419 StringTypes=StringTypes, SequenceTypes=SequenceTypes):
420 for item in sequence: 421 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 422 result.append(item) 423 else: 424 do_flatten(item, result)
425
426 -def flatten(obj, isinstance=isinstance, StringTypes=StringTypes, 427 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
428 """Flatten a sequence to a non-nested list. 429 430 Flatten() converts either a single scalar or a nested sequence 431 to a non-nested list. Note that flatten() considers strings 432 to be scalars instead of sequences like Python would. 433 """ 434 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes): 435 return [obj] 436 result = [] 437 for item in obj: 438 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 439 result.append(item) 440 else: 441 do_flatten(item, result) 442 return result
443
444 -def flatten_sequence(sequence, isinstance=isinstance, StringTypes=StringTypes, 445 SequenceTypes=SequenceTypes, do_flatten=do_flatten):
446 """Flatten a sequence to a non-nested list. 447 448 Same as flatten(), but it does not handle the single scalar 449 case. This is slightly more efficient when one knows that 450 the sequence to flatten can not be a scalar. 451 """ 452 result = [] 453 for item in sequence: 454 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes): 455 result.append(item) 456 else: 457 do_flatten(item, result) 458 return result
459 460 # Generic convert-to-string functions that abstract away whether or 461 # not the Python we're executing has Unicode support. The wrapper 462 # to_String_for_signature() will use a for_signature() method if the 463 # specified object has one. 464 #
465 -def to_String(s, 466 isinstance=isinstance, str=str, 467 UserString=UserString, BaseStringTypes=BaseStringTypes):
468 if isinstance(s,BaseStringTypes): 469 # Early out when already a string! 470 return s 471 elif isinstance(s, UserString): 472 # s.data can only be either a unicode or a regular 473 # string. Please see the UserString initializer. 474 return s.data 475 else: 476 return str(s)
477
478 -def to_String_for_subst(s, 479 isinstance=isinstance, str=str, to_String=to_String, 480 BaseStringTypes=BaseStringTypes, SequenceTypes=SequenceTypes, 481 UserString=UserString):
482 483 # Note that the test cases are sorted by order of probability. 484 if isinstance(s, BaseStringTypes): 485 return s 486 elif isinstance(s, SequenceTypes): 487 return ' '.join([to_String_for_subst(e) for e in 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 __str__(self):
1036 return ' '.join(self.data)
1037 1038
1039 -class Selector(OrderedDict):
1040 """A callable ordered dictionary that maps file suffixes to 1041 dictionary values. We preserve the order in which items are added 1042 so that get_suffix() calls always return the first suffix added."""
1043 - def __call__(self, env, source, ext=None):
1044 if ext is None: 1045 try: 1046 ext = source[0].get_suffix() 1047 except IndexError: 1048 ext = "" 1049 try: 1050 return self[ext] 1051 except KeyError: 1052 # Try to perform Environment substitution on the keys of 1053 # the dictionary before giving up. 1054 s_dict = {} 1055 for (k,v) in self.items(): 1056 if k is not None: 1057 s_k = env.subst(k) 1058 if s_k in s_dict: 1059 # We only raise an error when variables point 1060 # to the same suffix. If one suffix is literal 1061 # and a variable suffix contains this literal, 1062 # the literal wins and we don't raise an error. 1063 raise KeyError(s_dict[s_k][0], k, s_k) 1064 s_dict[s_k] = (k,v) 1065 try: 1066 return s_dict[ext][1] 1067 except KeyError: 1068 try: 1069 return self[None] 1070 except KeyError: 1071 return None
1072 1073 1074 if sys.platform == 'cygwin': 1075 # On Cygwin, os.path.normcase() lies, so just report back the 1076 # fact that the underlying Windows OS is case-insensitive.
1077 - def case_sensitive_suffixes(s1, s2):
1078 return 0
1079 else:
1080 - def case_sensitive_suffixes(s1, s2):
1081 return (os.path.normcase(s1) != os.path.normcase(s2))
1082
1083 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1084 if pre: 1085 path, fn = os.path.split(os.path.normpath(fname)) 1086 if fn[:len(pre)] != pre: 1087 fname = os.path.join(path, pre + fn) 1088 # Only append a suffix if the suffix we're going to add isn't already 1089 # there, and if either we've been asked to ensure the specific suffix 1090 # is present or there's no suffix on it at all. 1091 if suf and fname[-len(suf):] != suf and \ 1092 (ensure_suffix or not splitext(fname)[1]): 1093 fname = fname + suf 1094 return fname
1095 1096 1097 1098 # From Tim Peters, 1099 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1100 # ASPN: Python Cookbook: Remove duplicates from a sequence 1101 # (Also in the printed Python Cookbook.) 1102
1103 -def unique(s):
1104 """Return a list of the elements in s, but without duplicates. 1105 1106 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3], 1107 unique("abcabc") some permutation of ["a", "b", "c"], and 1108 unique(([1, 2], [2, 3], [1, 2])) some permutation of 1109 [[2, 3], [1, 2]]. 1110 1111 For best speed, all sequence elements should be hashable. Then 1112 unique() will usually work in linear time. 1113 1114 If not possible, the sequence elements should enjoy a total 1115 ordering, and if list(s).sort() doesn't raise TypeError it's 1116 assumed that they do enjoy a total ordering. Then unique() will 1117 usually work in O(N*log2(N)) time. 1118 1119 If that's not possible either, the sequence elements must support 1120 equality-testing. Then unique() will usually work in quadratic 1121 time. 1122 """ 1123 1124 n = len(s) 1125 if n == 0: 1126 return [] 1127 1128 # Try using a dict first, as that's the fastest and will usually 1129 # work. If it doesn't work, it will usually fail quickly, so it 1130 # usually doesn't cost much to *try* it. It requires that all the 1131 # sequence elements be hashable, and support equality comparison. 1132 u = {} 1133 try: 1134 for x in s: 1135 u[x] = 1 1136 except TypeError: 1137 pass # move on to the next method 1138 else: 1139 return list(u.keys()) 1140 del u 1141 1142 # We can't hash all the elements. Second fastest is to sort, 1143 # which brings the equal elements together; then duplicates are 1144 # easy to weed out in a single pass. 1145 # NOTE: Python's list.sort() was designed to be efficient in the 1146 # presence of many duplicate elements. This isn't true of all 1147 # sort functions in all languages or libraries, so this approach 1148 # is more effective in Python than it may be elsewhere. 1149 try: 1150 t = sorted(s) 1151 except TypeError: 1152 pass # move on to the next method 1153 else: 1154 assert n > 0 1155 last = t[0] 1156 lasti = i = 1 1157 while i < n: 1158 if t[i] != last: 1159 t[lasti] = last = t[i] 1160 lasti = lasti + 1 1161 i = i + 1 1162 return t[:lasti] 1163 del t 1164 1165 # Brute force is all that's left. 1166 u = [] 1167 for x in s: 1168 if x not in u: 1169 u.append(x) 1170 return u
1171 1172 1173 1174 # From Alex Martelli, 1175 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 1176 # ASPN: Python Cookbook: Remove duplicates from a sequence 1177 # First comment, dated 2001/10/13. 1178 # (Also in the printed Python Cookbook.) 1179
1180 -def uniquer(seq, idfun=None):
1181 if idfun is None: 1182 def idfun(x): return x 1183 seen = {} 1184 result = [] 1185 for item in seq: 1186 marker = idfun(item) 1187 # in old Python versions: 1188 # if seen.has_key(marker) 1189 # but in new ones: 1190 if marker in seen: continue 1191 seen[marker] = 1 1192 result.append(item) 1193 return result
1194 1195 # A more efficient implementation of Alex's uniquer(), this avoids the 1196 # idfun() argument and function-call overhead by assuming that all 1197 # items in the sequence are hashable. 1198
1199 -def uniquer_hashables(seq):
1200 seen = {} 1201 result = [] 1202 for item in seq: 1203 #if not item in seen: 1204 if item not in seen: 1205 seen[item] = 1 1206 result.append(item) 1207 return result
1208 1209 1210 # Recipe 19.11 "Reading Lines with Continuation Characters", 1211 # by Alex Martelli, straight from the Python CookBook (2nd edition).
1212 -def logical_lines(physical_lines, joiner=''.join):
1213 logical_line = [] 1214 for line in physical_lines: 1215 stripped = line.rstrip() 1216 if stripped.endswith('\\'): 1217 # a line which continues w/the next physical line 1218 logical_line.append(stripped[:-1]) 1219 else: 1220 # a line which does not continue, end of logical line 1221 logical_line.append(line) 1222 yield joiner(logical_line) 1223 logical_line = [] 1224 if logical_line: 1225 # end of sequence implies end of last logical line 1226 yield joiner(logical_line)
1227 1228
1229 -class LogicalLines(object):
1230 """ Wrapper class for the logical_lines method. 1231 1232 Allows us to read all "logical" lines at once from a 1233 given file object. 1234 """ 1235
1236 - def __init__(self, fileobj):
1237 self.fileobj = fileobj
1238
1239 - def readlines(self):
1240 result = [l for l in logical_lines(self.fileobj)] 1241 return result
1242 1243
1244 -class UniqueList(UserList):
1245 - def __init__(self, seq = []):
1246 UserList.__init__(self, seq) 1247 self.unique = True
1248 - def __make_unique(self):
1249 if not self.unique: 1250 self.data = uniquer_hashables(self.data) 1251 self.unique = True
1252 - def __lt__(self, other):
1253 self.__make_unique() 1254 return UserList.__lt__(self, other)
1255 - def __le__(self, other):
1256 self.__make_unique() 1257 return UserList.__le__(self, other)
1258 - def __eq__(self, other):
1259 self.__make_unique() 1260 return UserList.__eq__(self, other)
1261 - def __ne__(self, other):
1262 self.__make_unique() 1263 return UserList.__ne__(self, other)
1264 - def __gt__(self, other):
1265 self.__make_unique() 1266 return UserList.__gt__(self, other)
1267 - def __ge__(self, other):
1268 self.__make_unique() 1269 return UserList.__ge__(self, other)
1270 - def __cmp__(self, other):
1271 self.__make_unique() 1272 return UserList.__cmp__(self, other)
1273 - def __len__(self):
1274 self.__make_unique() 1275 return UserList.__len__(self)
1276 - def __getitem__(self, i):
1277 self.__make_unique() 1278 return UserList.__getitem__(self, i)
1279 - def __setitem__(self, i, item):
1280 UserList.__setitem__(self, i, item) 1281 self.unique = False
1282 - def __getslice__(self, i, j):
1283 self.__make_unique() 1284 return UserList.__getslice__(self, i, j)
1285 - def __setslice__(self, i, j, other):
1286 UserList.__setslice__(self, i, j, other) 1287 self.unique = False
1288 - def __add__(self, other):
1289 result = UserList.__add__(self, other) 1290 result.unique = False 1291 return result
1292 - def __radd__(self, other):
1293 result = UserList.__radd__(self, other) 1294 result.unique = False 1295 return result
1296 - def __iadd__(self, other):
1297 result = UserList.__iadd__(self, other) 1298 result.unique = False 1299 return result
1300 - def __mul__(self, other):
1301 result = UserList.__mul__(self, other) 1302 result.unique = False 1303 return result
1304 - def __rmul__(self, other):
1305 result = UserList.__rmul__(self, other) 1306 result.unique = False 1307 return result
1308 - def __imul__(self, other):
1309 result = UserList.__imul__(self, other) 1310 result.unique = False 1311 return result
1312 - def append(self, item):
1313 UserList.append(self, item) 1314 self.unique = False
1315 - def insert(self, i):
1316 UserList.insert(self, i) 1317 self.unique = False
1318 - def count(self, item):
1319 self.__make_unique() 1320 return UserList.count(self, item)
1321 - def index(self, item):
1322 self.__make_unique() 1323 return UserList.index(self, item)
1324 - def reverse(self):
1325 self.__make_unique() 1326 UserList.reverse(self)
1327 - def sort(self, *args, **kwds):
1328 self.__make_unique() 1329 return UserList.sort(self, *args, **kwds)
1330 - def extend(self, other):
1331 UserList.extend(self, other) 1332 self.unique = False
1333 1334
1335 -class Unbuffered(object):
1336 """ 1337 A proxy class that wraps a file object, flushing after every write, 1338 and delegating everything else to the wrapped object. 1339 """
1340 - def __init__(self, file):
1341 self.file = file 1342 self.softspace = 0 ## backward compatibility; not supported in Py3k
1343 - def write(self, arg):
1344 try: 1345 self.file.write(arg) 1346 self.file.flush() 1347 except IOError: 1348 # Stdout might be connected to a pipe that has been closed 1349 # by now. The most likely reason for the pipe being closed 1350 # is that the user has press ctrl-c. It this is the case, 1351 # then SCons is currently shutdown. We therefore ignore 1352 # IOError's here so that SCons can continue and shutdown 1353 # properly so that the .sconsign is correctly written 1354 # before SCons exits. 1355 pass
1356 - def __getattr__(self, attr):
1357 return getattr(self.file, attr)
1358
1359 -def make_path_relative(path):
1360 """ makes an absolute path name to a relative pathname. 1361 """ 1362 if os.path.isabs(path): 1363 drive_s,path = os.path.splitdrive(path) 1364 1365 import re 1366 if not drive_s: 1367 path=re.compile("/*(.*)").findall(path)[0] 1368 else: 1369 path=path[1:] 1370 1371 assert( not os.path.isabs( path ) ), path 1372 return path
1373 1374 1375 1376 # The original idea for AddMethod() and RenameFunction() come from the 1377 # following post to the ActiveState Python Cookbook: 1378 # 1379 # ASPN: Python Cookbook : Install bound methods in an instance 1380 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613 1381 # 1382 # That code was a little fragile, though, so the following changes 1383 # have been wrung on it: 1384 # 1385 # * Switched the installmethod() "object" and "function" arguments, 1386 # so the order reflects that the left-hand side is the thing being 1387 # "assigned to" and the right-hand side is the value being assigned. 1388 # 1389 # * Changed explicit type-checking to the "try: klass = object.__class__" 1390 # block in installmethod() below so that it still works with the 1391 # old-style classes that SCons uses. 1392 # 1393 # * Replaced the by-hand creation of methods and functions with use of 1394 # the "new" module, as alluded to in Alex Martelli's response to the 1395 # following Cookbook post: 1396 # 1397 # ASPN: Python Cookbook : Dynamically added methods to a class 1398 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 1399
1400 -def AddMethod(obj, function, name=None):
1401 """ 1402 Adds either a bound method to an instance or the function itself (or an unbound method in Python 2) to a class. 1403 If name is ommited the name of the specified function 1404 is used by default. 1405 1406 Example:: 1407 1408 a = A() 1409 def f(self, x, y): 1410 self.z = x + y 1411 AddMethod(f, A, "add") 1412 a.add(2, 4) 1413 print(a.z) 1414 AddMethod(lambda self, i: self.l[i], a, "listIndex") 1415 print(a.listIndex(5)) 1416 """ 1417 if name is None: 1418 name = function.__name__ 1419 else: 1420 function = RenameFunction(function, name) 1421 1422 # Note the Python version checks - WLB 1423 # Python 3.3 dropped the 3rd parameter from types.MethodType 1424 if hasattr(obj, '__class__') and obj.__class__ is not type: 1425 # "obj" is an instance, so it gets a bound method. 1426 if sys.version_info[:2] > (3, 2): 1427 method = MethodType(function, obj) 1428 else: 1429 method = MethodType(function, obj, obj.__class__) 1430 else: 1431 # Handle classes 1432 method = function 1433 1434 setattr(obj, name, method)
1435
1436 -def RenameFunction(function, name):
1437 """ 1438 Returns a function identical to the specified function, but with 1439 the specified name. 1440 """ 1441 return FunctionType(function.__code__, 1442 function.__globals__, 1443 name, 1444 function.__defaults__)
1445 1446 1447 md5 = False 1448 1449
1450 -def MD5signature(s):
1451 return str(s)
1452 1453
1454 -def MD5filesignature(fname, chunksize=65536):
1455 with open(fname, "rb") as f: 1456 result = f.read() 1457 return result
1458 1459 try: 1460 import hashlib 1461 except ImportError: 1462 pass 1463 else: 1464 if hasattr(hashlib, 'md5'): 1465 md5 = True 1466
1467 - def MD5signature(s):
1468 """ 1469 Generate a String of Hex digits representing the md5 signature of the string 1470 :param s: either string or bytes. Normally should be bytes 1471 :return: String of hex digits 1472 """ 1473 m = hashlib.md5() 1474 1475 try: 1476 m.update(to_bytes(s)) 1477 except TypeError as e: 1478 m.update(to_bytes(str(s))) 1479 1480 return m.hexdigest()
1481
1482 - def MD5filesignature(fname, chunksize=65536):
1483 """ 1484 :param fname: 1485 :param chunksize: 1486 :return: String of Hex digits 1487 """ 1488 m = hashlib.md5() 1489 f = open(fname, "rb") 1490 while True: 1491 blck = f.read(chunksize) 1492 if not blck: 1493 break 1494 m.update(to_bytes(blck)) 1495 f.close() 1496 return m.hexdigest()
1497 1498
1499 -def MD5collect(signatures):
1500 """ 1501 Collects a list of signatures into an aggregate signature. 1502 1503 signatures - a list of signatures 1504 returns - the aggregate signature 1505 """ 1506 if len(signatures) == 1: 1507 return signatures[0] 1508 else: 1509 return MD5signature(', '.join(signatures))
1510 1511 1512
1513 -def silent_intern(x):
1514 """ 1515 Perform sys.intern() on the passed argument and return the result. 1516 If the input is ineligible (e.g. a unicode string) the original argument is 1517 returned and no exception is thrown. 1518 """ 1519 try: 1520 return sys.intern(x) 1521 except TypeError: 1522 return x
1523 1524 1525 1526 # From Dinu C. Gherman, 1527 # Python Cookbook, second edition, recipe 6.17, p. 277. 1528 # Also: 1529 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 1530 # ASPN: Python Cookbook: Null Object Design Pattern 1531 1532 #TODO??? class Null(object):
1533 -class Null(object):
1534 """ Null objects always and reliably "do nothing." """
1535 - def __new__(cls, *args, **kwargs):
1536 if not '_instance' in vars(cls): 1537 cls._instance = super(Null, cls).__new__(cls, *args, **kwargs) 1538 return cls._instance
1539 - def __init__(self, *args, **kwargs):
1540 pass
1541 - def __call__(self, *args, **kwargs):
1542 return self
1543 - def __repr__(self):
1544 return "Null(0x%08X)" % id(self)
1545 - def __nonzero__(self):
1546 return False
1547 - def __bool__(self):
1548 return False
1549 - def __getattr__(self, name):
1550 return self
1551 - def __setattr__(self, name, value):
1552 return self
1553 - def __delattr__(self, name):
1554 return self
1555
1556 -class NullSeq(Null):
1557 - def __len__(self):
1558 return 0
1559 - def __iter__(self):
1560 return iter(())
1561 - def __getitem__(self, i):
1562 return self
1563 - def __delitem__(self, i):
1564 return self
1565 - def __setitem__(self, i, v):
1566 return self
1567 1568 1569 del __revision__ 1570 1571
1572 -def to_bytes(s):
1573 if s is None: 1574 return b'None' 1575 if not PY3 and isinstance(s, UnicodeType): 1576 # PY2, must encode unicode 1577 return bytearray(s, 'utf-8') 1578 if isinstance (s, (bytes, bytearray)) or bytes is str: 1579 # Above case not covered here as py2 bytes and strings are the same 1580 return s 1581 return bytes(s, 'utf-8')
1582 1583
1584 -def to_str(s):
1585 if s is None: 1586 return 'None' 1587 if bytes is str or is_String(s): 1588 return s 1589 return str (s, 'utf-8')
1590 1591
1592 -def cmp(a, b):
1593 """ 1594 Define cmp because it's no longer available in python3 1595 Works under python 2 as well 1596 """ 1597 return (a > b) - (a < b)
1598 1599
1600 -def get_env_bool(env, name, default=False):
1601 """Get a value of env[name] converted to boolean. The value of env[name] is 1602 interpreted as follows: 'true', 'yes', 'y', 'on' (case insensitive) and 1603 anything convertible to int that yields non-zero integer are True values; 1604 '0', 'false', 'no', 'n' and 'off' (case insensitive) are False values. For 1605 all other cases, default value is returned. 1606 1607 :Parameters: 1608 - `env` - dict or dict-like object, a convainer with variables 1609 - `name` - name of the variable in env to be returned 1610 - `default` - returned when env[name] does not exist or can't be converted to bool 1611 """ 1612 try: 1613 var = env[name] 1614 except KeyError: 1615 return default 1616 try: 1617 return bool(int(var)) 1618 except ValueError: 1619 if str(var).lower() in ('true', 'yes', 'y', 'on'): 1620 return True 1621 elif str(var).lower() in ('false', 'no', 'n', 'off'): 1622 return False 1623 else: 1624 return default
1625 1626
1627 -def get_os_env_bool(name, default=False):
1628 """Same as get_env_bool(os.environ, name, default).""" 1629 return get_env_bool(os.environ, name, default)
1630 1631 # Local Variables: 1632 # tab-width:4 1633 # indent-tabs-mode:nil 1634 # End: 1635 # vim: set expandtab tabstop=4 shiftwidth=4: 1636