1 """SCons.Util
2
3 Various utility functions go here.
4 """
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
55
56
57
58
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={}):
74
75 _altsep = os.altsep
76 if _altsep is None and sys.platform == 'win32':
77
78 _altsep = '/'
79 if _altsep:
82 else:
84 return path.rfind(sep)
85
86
87
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
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
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
107 """Same as os.path.splitext() but faster."""
108 sep = rightmost_separator(path, os.sep)
109 dot = path.rfind('.')
110
111 if dot > sep and not containsOnly(path[dot:], "0123456789."):
112 return path[:dot],path[dot:]
113 else:
114 return path,""
115
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
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
155 return len(self.data) != 0
156
159
161 return ' '.join(map(str, self.data))
162
164 return iter(self.data)
165
167 result = [x(*args, **kwargs) for x in self.data]
168 return self.__class__(result)
169
173
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
182
183 if isinstance(index, slice):
184
185
186 indices = index.indices(len(self.data))
187 return self.__class__([self[x] for x in
188 range(*indices)])
189 else:
190
191 return self.data[index]
192
193
194 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
195
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
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
221
222
223
224
225
226
227 pass
228
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
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
277 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None):
278 """
279 Print a tree of nodes. This is like render_tree, except it prints
280 lines directly instead of creating a string representation in memory,
281 so that huge trees can be printed.
282
283 :Parameters:
284 - `root` - the root node of the tree
285 - `child_func` - the function called to get the children of a node
286 - `prune` - don't visit the same node twice
287 - `showtags` - print status information to the left of each node line
288 - `margin` - the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe.
289 - `visited` - a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune.
290 """
291
292 rname = str(root)
293
294
295
296 if visited is None:
297 visited = {}
298
299 if showtags:
300
301 if showtags == 2:
302 legend = (' E = exists\n' +
303 ' R = exists in repository only\n' +
304 ' b = implicit builder\n' +
305 ' B = explicit builder\n' +
306 ' S = side effect\n' +
307 ' P = precious\n' +
308 ' A = always build\n' +
309 ' C = current\n' +
310 ' N = no clean\n' +
311 ' H = no cache\n' +
312 '\n')
313 sys.stdout.write(legend)
314
315 tags = ['[']
316 tags.append(' E'[IDX(root.exists())])
317 tags.append(' R'[IDX(root.rexists() and not root.exists())])
318 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
319 [0,2][IDX(root.has_builder())]])
320 tags.append(' S'[IDX(root.side_effect)])
321 tags.append(' P'[IDX(root.precious)])
322 tags.append(' A'[IDX(root.always_build)])
323 tags.append(' C'[IDX(root.is_up_to_date())])
324 tags.append(' N'[IDX(root.noclean)])
325 tags.append(' H'[IDX(root.nocache)])
326 tags.append(']')
327
328 else:
329 tags = []
330
331 def MMM(m):
332 return [" ","| "][m]
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
357
358
359
360
361
362
363
364
365
366
367
368
369
370 DictTypes = (dict, UserDict)
371 ListTypes = (list, UserList)
372
373 try:
374
375 SequenceTypes = (list, tuple, UserList, MappingView)
376 except NameError:
377 SequenceTypes = (list, tuple, UserList)
378
379
380
381
382
383 try:
384 StringTypes = (str, unicode, UserString)
385 except NameError:
386 StringTypes = (str, UserString)
387
388
389
390 try:
391 BaseStringTypes = (str, unicode)
392 except NameError:
393 BaseStringTypes = (str)
394
397
400
403
404 -def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
405 return isinstance(obj, tuple)
406
409
418
426
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
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
462
463
464
465
469 if isinstance(s,BaseStringTypes):
470
471 return s
472 elif isinstance(s, UserString):
473
474
475 return s.data
476 else:
477 return str(s)
478
483
484
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
491
492 return s.data
493 else:
494 return str(s)
495
498 try:
499 f = obj.for_signature
500 except AttributeError:
501 if isinstance(obj, dict):
502
503
504
505 return pprint.pformat(obj, width=1000000)
506 else:
507 return to_String_for_subst(obj)
508 else:
509 return f()
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525 _semi_deepcopy_dispatch = d = {}
526
528 copy = {}
529 for key, val in x.items():
530
531
532
533
534
535
536 if key not in exclude:
537 copy[key] = semi_deepcopy(val)
538 return copy
539 d[dict] = semi_deepcopy_dict
540
542 return list(map(semi_deepcopy, x))
543 d[list] = _semi_deepcopy_list
544
546 return tuple(map(semi_deepcopy, x))
547 d[tuple] = _semi_deepcopy_tuple
548
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
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
596 """Wrap an object as a Proxy object"""
597 self._subject = subject
598
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
605 """Retrieve the entire wrapped object"""
606 return self._subject
607
609 if issubclass(other.__class__, self._subject.__class__):
610 return self._subject == other
611 return self.__dict__ == other.__dict__
612
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 """
621 self.attribute = attribute
623 if isinstance(obj, cls):
624 return getattr(obj._subject, self.attribute)
625 else:
626 return self
627
628
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:
658 RegError = _NoError
659
660
661
662
663
664
665
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
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
701
702 p = key.rfind('\\') + 1
703 keyp = key[:p-1]
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
715
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
804
805
806
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 ]
850 else:
851 newpaths = newpath
852
853 if canonicalize:
854 newpaths=list(map(canonicalize, newpaths))
855
856 if not delete_existing:
857
858
859
860
861
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()
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
883
884 normpaths = []
885 paths = []
886
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 ]
931 else:
932 newpaths = newpath
933
934 if canonicalize:
935 newpaths=list(map(canonicalize, newpaths))
936
937 if not delete_existing:
938
939
940
941
942
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
960
961 newpaths = paths + newpaths
962 newpaths.reverse()
963
964 normpaths = []
965 paths = []
966
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
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':
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:
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
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
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 """
1041 return ' '.join(self.data)
1042
1043
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
1058
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
1065
1066
1067
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
1081
1084 else:
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
1094
1095
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
1104
1105
1106
1107
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
1134
1135
1136
1137 u = {}
1138 try:
1139 for x in s:
1140 u[x] = 1
1141 except TypeError:
1142 pass
1143 else:
1144 return list(u.keys())
1145 del u
1146
1147
1148
1149
1150
1151
1152
1153
1154 try:
1155 t = sorted(s)
1156 except TypeError:
1157 pass
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
1171 u = []
1172 for x in s:
1173 if x not in u:
1174 u.append(x)
1175 return u
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
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
1196
1197
1198 if marker in seen: continue
1199 seen[marker] = 1
1200 result.append(item)
1201 return result
1202
1203
1204
1205
1206
1208 seen = {}
1209 result = []
1210 for item in seq:
1211
1212 if item not in seen:
1213 seen[item] = 1
1214 result.append(item)
1215 return result
1216
1217
1218
1219
1221 logical_line = []
1222 for line in physical_lines:
1223 stripped = line.rstrip()
1224 if stripped.endswith('\\'):
1225
1226 logical_line.append(stripped[:-1])
1227 else:
1228
1229 logical_line.append(line)
1230 yield joiner(logical_line)
1231 logical_line = []
1232 if logical_line:
1233
1234 yield joiner(logical_line)
1235
1236
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
1245 self.fileobj = fileobj
1246
1248 result = [l for l in logical_lines(self.fileobj)]
1249 return result
1250
1251
1254 UserList.__init__(self, seq)
1255 self.unique = True
1257 if not self.unique:
1258 self.data = uniquer_hashables(self.data)
1259 self.unique = True
1288 UserList.__setitem__(self, i, item)
1289 self.unique = False
1294 UserList.__setslice__(self, i, j, other)
1295 self.unique = False
1321 UserList.append(self, item)
1322 self.unique = False
1324 UserList.insert(self, i)
1325 self.unique = False
1335 - def sort(self, *args, **kwds):
1339 UserList.extend(self, other)
1340 self.unique = False
1341
1342
1344 """
1345 A proxy class that wraps a file object, flushing after every write,
1346 and delegating everything else to the wrapped object.
1347 """
1349 self.file = file
1350 self.softspace = 0
1352 try:
1353 self.file.write(arg)
1354 self.file.flush()
1355 except IOError:
1356
1357
1358
1359
1360
1361
1362
1363 pass
1365 return getattr(self.file, attr)
1366
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
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
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
1431
1432 if hasattr(obj, '__class__') and obj.__class__ is not type:
1433
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
1440 method = function
1441
1442 setattr(obj, name, method)
1443
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
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
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
1492
1493 md5 = False
1494
1497
1502
1503
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
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
1531
1532
1533
1534
1535
1536
1537 -class Null(object):
1538 """ Null objects always and reliably "do nothing." """
1539 - def __new__(cls, *args, **kwargs):
1548 return "Null(0x%08X)" % id(self)
1559
1571
1572
1573 del __revision__
1574
1575
1577 if s is None:
1578 return b'None'
1579 if not PY3 and isinstance(s, UnicodeType):
1580
1581 return bytearray(s, 'utf-8')
1582 if isinstance (s, (bytes, bytearray)) or bytes is str:
1583
1584 return s
1585 return bytes(s, 'utf-8')
1586
1587
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
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
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
1632 """Same as get_env_bool(os.environ, name, default)."""
1633 return get_env_bool(os.environ, name, default)
1634
1635
1636
1637
1638
1639
1640