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