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 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
54
55
56
57
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={}):
73
74 _altsep = os.altsep
75 if _altsep is None and sys.platform == 'win32':
76
77 _altsep = '/'
78 if _altsep:
81 else:
83 return path.rfind(sep)
84
85
86
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
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
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
106 """Same as os.path.splitext() but faster."""
107 sep = rightmost_separator(path, os.sep)
108 dot = path.rfind('.')
109
110 if dot > sep and not containsOnly(path[dot:], "0123456789."):
111 return path[:dot],path[dot:]
112 else:
113 return path,""
114
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
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
154 return len(self.data) != 0
155
158
160 return ' '.join(map(str, self.data))
161
163 return iter(self.data)
164
166 result = [x(*args, **kwargs) for x in self.data]
167 return self.__class__(result)
168
172
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
181
182 if isinstance(index, slice):
183
184
185 indices = index.indices(len(self.data))
186 return self.__class__([self[x] for x in
187 range(*indices)])
188 else:
189
190 return self.data[index]
191
192
193 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
194
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
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
220
221
222
223
224
225
226 pass
227
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
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
276 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None):
277 """
278 Print a tree of nodes. This is like render_tree, except it prints
279 lines directly instead of creating a string representation in memory,
280 so that huge trees can be printed.
281
282 :Parameters:
283 - `root` - the root node of the tree
284 - `child_func` - the function called to get the children of a node
285 - `prune` - don't visit the same node twice
286 - `showtags` - print status information to the left of each node line
287 - `margin` - the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe.
288 - `visited` - a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune.
289 """
290
291 rname = str(root)
292
293
294
295 if visited is None:
296 visited = {}
297
298 if showtags:
299
300 if showtags == 2:
301 legend = (' E = exists\n' +
302 ' R = exists in repository only\n' +
303 ' b = implicit builder\n' +
304 ' B = explicit builder\n' +
305 ' S = side effect\n' +
306 ' P = precious\n' +
307 ' A = always build\n' +
308 ' C = current\n' +
309 ' N = no clean\n' +
310 ' H = no cache\n' +
311 '\n')
312 sys.stdout.write(legend)
313
314 tags = ['[']
315 tags.append(' E'[IDX(root.exists())])
316 tags.append(' R'[IDX(root.rexists() and not root.exists())])
317 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
318 [0,2][IDX(root.has_builder())]])
319 tags.append(' S'[IDX(root.side_effect)])
320 tags.append(' P'[IDX(root.precious)])
321 tags.append(' A'[IDX(root.always_build)])
322 tags.append(' C'[IDX(root.is_up_to_date())])
323 tags.append(' N'[IDX(root.noclean)])
324 tags.append(' H'[IDX(root.nocache)])
325 tags.append(']')
326
327 else:
328 tags = []
329
330 def MMM(m):
331 return [" ","| "][m]
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
356
357
358
359
360
361
362
363
364
365
366
367
368
369 DictTypes = (dict, UserDict)
370 ListTypes = (list, UserList)
371
372 try:
373
374 SequenceTypes = (list, tuple, UserList, MappingView)
375 except NameError:
376 SequenceTypes = (list, tuple, UserList)
377
378
379
380
381
382 try:
383 StringTypes = (str, unicode, UserString)
384 except NameError:
385 StringTypes = (str, UserString)
386
387
388
389 try:
390 BaseStringTypes = (str, unicode)
391 except NameError:
392 BaseStringTypes = (str)
393
396
399
402
403 -def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
404 return isinstance(obj, tuple)
405
408
417
425
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
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
461
462
463
464
468 if isinstance(s,BaseStringTypes):
469
470 return s
471 elif isinstance(s, UserString):
472
473
474 return s.data
475 else:
476 return str(s)
477
482
483
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
490
491 return s.data
492 else:
493 return str(s)
494
497 try:
498 f = obj.for_signature
499 except AttributeError:
500 if isinstance(obj, dict):
501
502
503
504 return pprint.pformat(obj, width=1000000)
505 else:
506 return to_String_for_subst(obj)
507 else:
508 return f()
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 _semi_deepcopy_dispatch = d = {}
525
527 copy = {}
528 for key, val in x.items():
529
530
531
532
533
534
535 if key not in exclude:
536 copy[key] = semi_deepcopy(val)
537 return copy
538 d[dict] = semi_deepcopy_dict
539
541 return list(map(semi_deepcopy, x))
542 d[list] = _semi_deepcopy_list
543
545 return tuple(map(semi_deepcopy, x))
546 d[tuple] = _semi_deepcopy_tuple
547
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
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
595 """Wrap an object as a Proxy object"""
596 self._subject = subject
597
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
604 """Retrieve the entire wrapped object"""
605 return self._subject
606
608 if issubclass(other.__class__, self._subject.__class__):
609 return self._subject == other
610 return self.__dict__ == other.__dict__
611
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 """
620 self.attribute = attribute
622 if isinstance(obj, cls):
623 return getattr(obj._subject, self.attribute)
624 else:
625 return self
626
627
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:
657 RegError = _NoError
658
659 WinError = None
660
661
662
663
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
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
698
699 p = key.rfind('\\') + 1
700 keyp = key[:p-1]
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
712
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
801
802
803
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 ]
847 else:
848 newpaths = newpath
849
850 if canonicalize:
851 newpaths=list(map(canonicalize, newpaths))
852
853 if not delete_existing:
854
855
856
857
858
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()
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
880
881 normpaths = []
882 paths = []
883
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 ]
928 else:
929 newpaths = newpath
930
931 if canonicalize:
932 newpaths=list(map(canonicalize, newpaths))
933
934 if not delete_existing:
935
936
937
938
939
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
957
958 newpaths = paths + newpaths
959 newpaths.reverse()
960
961 normpaths = []
962 paths = []
963
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
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':
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:
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
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
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 """
1036 return ' '.join(self.data)
1037
1038
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
1053
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
1060
1061
1062
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
1076
1079 else:
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
1089
1090
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
1099
1100
1101
1102
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
1129
1130
1131
1132 u = {}
1133 try:
1134 for x in s:
1135 u[x] = 1
1136 except TypeError:
1137 pass
1138 else:
1139 return list(u.keys())
1140 del u
1141
1142
1143
1144
1145
1146
1147
1148
1149 try:
1150 t = sorted(s)
1151 except TypeError:
1152 pass
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
1166 u = []
1167 for x in s:
1168 if x not in u:
1169 u.append(x)
1170 return u
1171
1172
1173
1174
1175
1176
1177
1178
1179
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
1188
1189
1190 if marker in seen: continue
1191 seen[marker] = 1
1192 result.append(item)
1193 return result
1194
1195
1196
1197
1198
1200 seen = {}
1201 result = []
1202 for item in seq:
1203
1204 if item not in seen:
1205 seen[item] = 1
1206 result.append(item)
1207 return result
1208
1209
1210
1211
1213 logical_line = []
1214 for line in physical_lines:
1215 stripped = line.rstrip()
1216 if stripped.endswith('\\'):
1217
1218 logical_line.append(stripped[:-1])
1219 else:
1220
1221 logical_line.append(line)
1222 yield joiner(logical_line)
1223 logical_line = []
1224 if logical_line:
1225
1226 yield joiner(logical_line)
1227
1228
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
1237 self.fileobj = fileobj
1238
1240 result = [l for l in logical_lines(self.fileobj)]
1241 return result
1242
1243
1246 UserList.__init__(self, seq)
1247 self.unique = True
1249 if not self.unique:
1250 self.data = uniquer_hashables(self.data)
1251 self.unique = True
1280 UserList.__setitem__(self, i, item)
1281 self.unique = False
1286 UserList.__setslice__(self, i, j, other)
1287 self.unique = False
1313 UserList.append(self, item)
1314 self.unique = False
1316 UserList.insert(self, i)
1317 self.unique = False
1327 - def sort(self, *args, **kwds):
1331 UserList.extend(self, other)
1332 self.unique = False
1333
1334
1336 """
1337 A proxy class that wraps a file object, flushing after every write,
1338 and delegating everything else to the wrapped object.
1339 """
1341 self.file = file
1342 self.softspace = 0
1344 try:
1345 self.file.write(arg)
1346 self.file.flush()
1347 except IOError:
1348
1349
1350
1351
1352
1353
1354
1355 pass
1357 return getattr(self.file, attr)
1358
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
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
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
1423
1424 if hasattr(obj, '__class__') and obj.__class__ is not type:
1425
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
1432 method = function
1433
1434 setattr(obj, name, method)
1435
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
1452
1453
1458
1459 try:
1460 import hashlib
1461 except ImportError:
1462 pass
1463 else:
1464 if hasattr(hashlib, 'md5'):
1465 md5 = True
1466
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
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
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
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
1527
1528
1529
1530
1531
1532
1533 -class Null(object):
1534 """ Null objects always and reliably "do nothing." """
1535 - def __new__(cls, *args, **kwargs):
1544 return "Null(0x%08X)" % id(self)
1555
1567
1568
1569 del __revision__
1570
1571
1573 if s is None:
1574 return b'None'
1575 if not PY3 and isinstance(s, UnicodeType):
1576
1577 return bytearray(s, 'utf-8')
1578 if isinstance (s, (bytes, bytearray)) or bytes is str:
1579
1580 return s
1581 return bytes(s, 'utf-8')
1582
1583
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
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
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
1628 """Same as get_env_bool(os.environ, name, default)."""
1629 return get_env_bool(os.environ, name, default)
1630
1631
1632
1633
1634
1635
1636