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 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog"
28
29 import os
30 import sys
31 import copy
32 import re
33 import types
34 import codecs
35 import pprint
36
37 PY3 = sys.version_info[0] == 3
38
39 try:
40 from UserDict import UserDict
41 except ImportError as e:
42 from collections import UserDict
43
44 try:
45 from UserList import UserList
46 except ImportError as e:
47 from collections import UserList
48
49 from collections import Iterable
50
51 try:
52 from UserString import UserString
53 except ImportError as e:
54 from collections import UserString
55
56
57
58
59
60
61
62 MethodType = types.MethodType
63 FunctionType = types.FunctionType
64
65 try:
66 unicode
67 except NameError:
68 UnicodeType = str
69 else:
70 UnicodeType = unicode
71
72 -def dictify(keys, values, result={}):
76
77 _altsep = os.altsep
78 if _altsep is None and sys.platform == 'win32':
79
80 _altsep = '/'
81 if _altsep:
84 else:
86 return path.rfind(sep)
87
88
89
91 """Check whether sequence str contains ANY of the items in set."""
92 for c in set:
93 if c in str: return 1
94 return 0
95
97 """Check whether sequence str contains ALL of the items in set."""
98 for c in set:
99 if c not in str: return 0
100 return 1
101
103 """Check whether sequence str contains ONLY items in set."""
104 for c in str:
105 if c not in set: return 0
106 return 1
107
109 "Same as os.path.splitext() but faster."
110 sep = rightmost_separator(path, os.sep)
111 dot = path.rfind('.')
112
113 if dot > sep and not containsOnly(path[dot:], "0123456789."):
114 return path[:dot],path[dot:]
115 else:
116 return path,""
117
119 """
120 Make the drive letter (if any) upper case.
121 This is useful because Windows is inconsistent on the case
122 of the drive letter, which can cause inconsistencies when
123 calculating command signatures.
124 """
125 drive, rest = os.path.splitdrive(path)
126 if drive:
127 path = drive.upper() + rest
128 return path
129
131 """This class is almost exactly like a regular list of Nodes
132 (actually it can hold any object), with one important difference.
133 If you try to get an attribute from this list, it will return that
134 attribute from every item in the list. For example:
135
136 >>> someList = NodeList([ ' foo ', ' bar ' ])
137 >>> someList.strip()
138 [ 'foo', 'bar' ]
139 """
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
157 return len(self.data) != 0
158
161
163 return ' '.join(map(str, self.data))
164
166 return iter(self.data)
167
169 result = [x(*args, **kwargs) for x in self.data]
170 return self.__class__(result)
171
175
177 """
178 This comes for free on py2,
179 but py3 slices of NodeList are returning a list
180 breaking slicing nodelist and refering to
181 properties and methods on contained object
182 """
183
184
185 if isinstance(index, slice):
186
187
188 indices = index.indices(len(self.data))
189 return self.__class__([self[x] for x in
190 range(*indices)])
191 else:
192
193 return self.data[index]
194
195
196 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
197
199 """Given a string, first determine if it looks like a reference
200 to a single environment variable, like "$FOO" or "${FOO}".
201 If so, return that variable with no decorations ("FOO").
202 If not, return None."""
203 mo=_get_env_var.match(to_String(varstr))
204 if mo:
205 var = mo.group(1)
206 if var[0] == '{':
207 return var[1:-1]
208 else:
209 return var
210 else:
211 return None
212
214 print_it = True
215 - def __call__(self, text, append_newline=1):
216 if not self.print_it:
217 return
218 if append_newline: text = text + '\n'
219 try:
220 sys.stdout.write(UnicodeType(text))
221 except IOError:
222
223
224
225
226
227
228
229 pass
230
233
234
235 -def render_tree(root, child_func, prune=0, margin=[0], visited=None):
236 """
237 Render a tree of nodes into an ASCII tree view.
238
239 :Parameters:
240 - `root`: the root node of the tree
241 - `child_func`: the function called to get the children of a node
242 - `prune`: don't visit the same node twice
243 - `margin`: the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe.
244 - `visited`: a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune.
245 """
246
247 rname = str(root)
248
249
250 if visited is None:
251 visited = {}
252
253 children = child_func(root)
254 retval = ""
255 for pipe in margin[:-1]:
256 if pipe:
257 retval = retval + "| "
258 else:
259 retval = retval + " "
260
261 if rname in visited:
262 return retval + "+-[" + rname + "]\n"
263
264 retval = retval + "+-" + rname + "\n"
265 if not prune:
266 visited = copy.copy(visited)
267 visited[rname] = 1
268
269 for i in range(len(children)):
270 margin.append(i < len(children)-1)
271 retval = retval + render_tree(children[i], child_func, prune, margin, visited)
272 margin.pop()
273
274 return retval
275
276 IDX = lambda N: N and 1 or 0
277
278
279 -def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None):
280 """
281 Print a tree of nodes. This is like render_tree, except it prints
282 lines directly instead of creating a string representation in memory,
283 so that huge trees can be printed.
284
285 :Parameters:
286 - `root` - the root node of the tree
287 - `child_func` - the function called to get the children of a node
288 - `prune` - don't visit the same node twice
289 - `showtags` - print status information to the left of each node line
290 - `margin` - the format of the left margin to use for children of root. 1 results in a pipe, and 0 results in no pipe.
291 - `visited` - a dictionary of visited nodes in the current branch if not prune, or in the whole tree if prune.
292 """
293
294 rname = str(root)
295
296
297
298 if visited is None:
299 visited = {}
300
301 if showtags:
302
303 if showtags == 2:
304 legend = (' E = exists\n' +
305 ' R = exists in repository only\n' +
306 ' b = implicit builder\n' +
307 ' B = explicit builder\n' +
308 ' S = side effect\n' +
309 ' P = precious\n' +
310 ' A = always build\n' +
311 ' C = current\n' +
312 ' N = no clean\n' +
313 ' H = no cache\n' +
314 '\n')
315 sys.stdout.write(legend)
316
317 tags = ['[']
318 tags.append(' E'[IDX(root.exists())])
319 tags.append(' R'[IDX(root.rexists() and not root.exists())])
320 tags.append(' BbB'[[0,1][IDX(root.has_explicit_builder())] +
321 [0,2][IDX(root.has_builder())]])
322 tags.append(' S'[IDX(root.side_effect)])
323 tags.append(' P'[IDX(root.precious)])
324 tags.append(' A'[IDX(root.always_build)])
325 tags.append(' C'[IDX(root.is_up_to_date())])
326 tags.append(' N'[IDX(root.noclean)])
327 tags.append(' H'[IDX(root.nocache)])
328 tags.append(']')
329
330 else:
331 tags = []
332
333 def MMM(m):
334 return [" ","| "][m]
335 margins = list(map(MMM, margin[:-1]))
336
337 children = child_func(root)
338
339 if prune and rname in visited and children:
340 sys.stdout.write(''.join(tags + margins + ['+-[', rname, ']']) + '\n')
341 return
342
343 sys.stdout.write(''.join(tags + margins + ['+-', rname]) + '\n')
344
345 visited[rname] = 1
346
347 if children:
348 margin.append(1)
349 idx = IDX(showtags)
350 for C in children[:-1]:
351 print_tree(C, child_func, prune, idx, margin, visited)
352 margin[-1] = 0
353 print_tree(children[-1], child_func, prune, idx, margin, visited)
354 margin.pop()
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 DictTypes = (dict, UserDict)
373 ListTypes = (list, UserList)
374 SequenceTypes = (list, tuple, UserList)
375
376
377
378
379 try:
380 StringTypes = (str, unicode, UserString)
381 except NameError:
382 StringTypes = (str, UserString)
383
384
385
386 try:
387 BaseStringTypes = (str, unicode)
388 except NameError:
389 BaseStringTypes = (str)
390
393
396
399
400 -def is_Tuple(obj, isinstance=isinstance, tuple=tuple):
401 return isinstance(obj, tuple)
402
405
414
422
425 """Flatten a sequence to a non-nested list.
426
427 Flatten() converts either a single scalar or a nested sequence
428 to a non-nested list. Note that flatten() considers strings
429 to be scalars instead of sequences like Python would.
430 """
431 if isinstance(obj, StringTypes) or not isinstance(obj, SequenceTypes):
432 return [obj]
433 result = []
434 for item in obj:
435 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
436 result.append(item)
437 else:
438 do_flatten(item, result)
439 return result
440
443 """Flatten a sequence to a non-nested list.
444
445 Same as flatten(), but it does not handle the single scalar
446 case. This is slightly more efficient when one knows that
447 the sequence to flatten can not be a scalar.
448 """
449 result = []
450 for item in sequence:
451 if isinstance(item, StringTypes) or not isinstance(item, SequenceTypes):
452 result.append(item)
453 else:
454 do_flatten(item, result)
455 return result
456
457
458
459
460
461
465 if isinstance(s,BaseStringTypes):
466
467 return s
468 elif isinstance(s, UserString):
469
470
471 return s.data
472 else:
473 return str(s)
474
479
480
481 if isinstance(s, BaseStringTypes):
482 return s
483 elif isinstance(s, SequenceTypes):
484 return ' '.join([to_String_for_subst(e) for e in s])
485 elif isinstance(s, UserString):
486
487
488 return s.data
489 else:
490 return str(s)
491
494 try:
495 f = obj.for_signature
496 except AttributeError:
497 if isinstance(obj, dict):
498
499
500
501 return pprint.pformat(obj, width=1000000)
502 else:
503 return to_String_for_subst(obj)
504 else:
505 return f()
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521 _semi_deepcopy_dispatch = d = {}
522
524 copy = {}
525 for key, val in x.items():
526
527
528
529
530
531
532 if key not in exclude:
533 copy[key] = semi_deepcopy(val)
534 return copy
535 d[dict] = semi_deepcopy_dict
536
538 return list(map(semi_deepcopy, x))
539 d[list] = _semi_deepcopy_list
540
542 return tuple(map(semi_deepcopy, x))
543 d[tuple] = _semi_deepcopy_tuple
544
546 copier = _semi_deepcopy_dispatch.get(type(x))
547 if copier:
548 return copier(x)
549 else:
550 if hasattr(x, '__semi_deepcopy__') and callable(x.__semi_deepcopy__):
551 return x.__semi_deepcopy__()
552 elif isinstance(x, UserDict):
553 return x.__class__(semi_deepcopy_dict(x))
554 elif isinstance(x, UserList):
555 return x.__class__(_semi_deepcopy_list(x))
556
557 return x
558
559
561 """A simple generic Proxy class, forwarding all calls to
562 subject. So, for the benefit of the python newbie, what does
563 this really mean? Well, it means that you can take an object, let's
564 call it 'objA', and wrap it in this Proxy class, with a statement
565 like this
566
567 proxyObj = Proxy(objA),
568
569 Then, if in the future, you do something like this
570
571 x = proxyObj.var1,
572
573 since Proxy does not have a 'var1' attribute (but presumably objA does),
574 the request actually is equivalent to saying
575
576 x = objA.var1
577
578 Inherit from this class to create a Proxy.
579
580 Note that, with new-style classes, this does *not* work transparently
581 for Proxy subclasses that use special .__*__() method names, because
582 those names are now bound to the class, not the individual instances.
583 You now need to know in advance which .__*__() method names you want
584 to pass on to the underlying Proxy object, and specifically delegate
585 their calls like this:
586
587 class Foo(Proxy):
588 __str__ = Delegate('__str__')
589 """
590
592 """Wrap an object as a Proxy object"""
593 self._subject = subject
594
596 """Retrieve an attribute from the wrapped object. If the named
597 attribute doesn't exist, AttributeError is raised"""
598 return getattr(self._subject, name)
599
601 """Retrieve the entire wrapped object"""
602 return self._subject
603
605 if issubclass(other.__class__, self._subject.__class__):
606 return self._subject == other
607 return self.__dict__ == other.__dict__
608
610 """A Python Descriptor class that delegates attribute fetches
611 to an underlying wrapped subject of a Proxy. Typical use:
612
613 class Foo(Proxy):
614 __str__ = Delegate('__str__')
615 """
617 self.attribute = attribute
619 if isinstance(obj, cls):
620 return getattr(obj._subject, self.attribute)
621 else:
622 return self
623
624
625 can_read_reg = 0
626 try:
627 import winreg
628
629 can_read_reg = 1
630 hkey_mod = winreg
631
632 RegOpenKeyEx = winreg.OpenKeyEx
633 RegEnumKey = winreg.EnumKey
634 RegEnumValue = winreg.EnumValue
635 RegQueryValueEx = winreg.QueryValueEx
636 RegError = winreg.error
637
638 except ImportError:
639 try:
640 import win32api
641 import win32con
642 can_read_reg = 1
643 hkey_mod = win32con
644
645 RegOpenKeyEx = win32api.RegOpenKeyEx
646 RegEnumKey = win32api.RegEnumKey
647 RegEnumValue = win32api.RegEnumValue
648 RegQueryValueEx = win32api.RegQueryValueEx
649 RegError = win32api.error
650
651 except ImportError:
654 RegError = _NoError
655
656 WinError = None
657
658
659
660
663 try:
664 WinError = WindowsError
665 except NameError:
666 WinError = PlainWindowsError
667
668
669 if can_read_reg:
670 HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT
671 HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
672 HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
673 HKEY_USERS = hkey_mod.HKEY_USERS
674
676 """This utility function returns a value in the registry
677 without having to open the key first. Only available on
678 Windows platforms with a version of Python that can read the
679 registry. Returns the same thing as
680 SCons.Util.RegQueryValueEx, except you just specify the entire
681 path to the value, and don't have to bother opening the key
682 first. So:
683
684 Instead of:
685 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
686 r'SOFTWARE\Microsoft\Windows\CurrentVersion')
687 out = SCons.Util.RegQueryValueEx(k,
688 'ProgramFilesDir')
689
690 You can write:
691 out = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
692 r'SOFTWARE\Microsoft\Windows\CurrentVersion\ProgramFilesDir')
693 """
694
695
696 p = key.rfind('\\') + 1
697 keyp = key[:p-1]
698 val = key[p:]
699 k = RegOpenKeyEx(root, keyp)
700 return RegQueryValueEx(k,val)
701 else:
702 HKEY_CLASSES_ROOT = None
703 HKEY_LOCAL_MACHINE = None
704 HKEY_CURRENT_USER = None
705 HKEY_USERS = None
706
709
712
713 if sys.platform == 'win32':
714
715 - def WhereIs(file, path=None, pathext=None, reject=[]):
716 if path is None:
717 try:
718 path = os.environ['PATH']
719 except KeyError:
720 return None
721 if is_String(path):
722 path = path.split(os.pathsep)
723 if pathext is None:
724 try:
725 pathext = os.environ['PATHEXT']
726 except KeyError:
727 pathext = '.COM;.EXE;.BAT;.CMD'
728 if is_String(pathext):
729 pathext = pathext.split(os.pathsep)
730 for ext in pathext:
731 if ext.lower() == file[-len(ext):].lower():
732 pathext = ['']
733 break
734 if not is_List(reject) and not is_Tuple(reject):
735 reject = [reject]
736 for dir in path:
737 f = os.path.join(dir, file)
738 for ext in pathext:
739 fext = f + ext
740 if os.path.isfile(fext):
741 try:
742 reject.index(fext)
743 except ValueError:
744 return os.path.normpath(fext)
745 continue
746 return None
747
748 elif os.name == 'os2':
749
750 - def WhereIs(file, path=None, pathext=None, reject=[]):
751 if path is None:
752 try:
753 path = os.environ['PATH']
754 except KeyError:
755 return None
756 if is_String(path):
757 path = path.split(os.pathsep)
758 if pathext is None:
759 pathext = ['.exe', '.cmd']
760 for ext in pathext:
761 if ext.lower() == file[-len(ext):].lower():
762 pathext = ['']
763 break
764 if not is_List(reject) and not is_Tuple(reject):
765 reject = [reject]
766 for dir in path:
767 f = os.path.join(dir, file)
768 for ext in pathext:
769 fext = f + ext
770 if os.path.isfile(fext):
771 try:
772 reject.index(fext)
773 except ValueError:
774 return os.path.normpath(fext)
775 continue
776 return None
777
778 else:
779
780 - def WhereIs(file, path=None, pathext=None, reject=[]):
781 import stat
782 if path is None:
783 try:
784 path = os.environ['PATH']
785 except KeyError:
786 return None
787 if is_String(path):
788 path = path.split(os.pathsep)
789 if not is_List(reject) and not is_Tuple(reject):
790 reject = [reject]
791 for d in path:
792 f = os.path.join(d, file)
793 if os.path.isfile(f):
794 try:
795 st = os.stat(f)
796 except OSError:
797
798
799
800
801 continue
802 if stat.S_IMODE(st[stat.ST_MODE]) & 0o111:
803 try:
804 reject.index(f)
805 except ValueError:
806 return os.path.normpath(f)
807 continue
808 return None
809
810 -def PrependPath(oldpath, newpath, sep = os.pathsep,
811 delete_existing=1, canonicalize=None):
812 """This prepends newpath elements to the given oldpath. Will only
813 add any particular path once (leaving the first one it encounters
814 and ignoring the rest, to preserve path order), and will
815 os.path.normpath and os.path.normcase all paths to help assure
816 this. This can also handle the case where the given old path
817 variable is a list instead of a string, in which case a list will
818 be returned instead of a string.
819
820 Example:
821 Old Path: "/foo/bar:/foo"
822 New Path: "/biz/boom:/foo"
823 Result: "/biz/boom:/foo:/foo/bar"
824
825 If delete_existing is 0, then adding a path that exists will
826 not move it to the beginning; it will stay where it is in the
827 list.
828
829 If canonicalize is not None, it is applied to each element of
830 newpath before use.
831 """
832
833 orig = oldpath
834 is_list = 1
835 paths = orig
836 if not is_List(orig) and not is_Tuple(orig):
837 paths = paths.split(sep)
838 is_list = 0
839
840 if is_String(newpath):
841 newpaths = newpath.split(sep)
842 elif not is_List(newpath) and not is_Tuple(newpath):
843 newpaths = [ newpath ]
844 else:
845 newpaths = newpath
846
847 if canonicalize:
848 newpaths=list(map(canonicalize, newpaths))
849
850 if not delete_existing:
851
852
853
854
855
856 result = []
857 normpaths = []
858 for path in paths:
859 if not path:
860 continue
861 normpath = os.path.normpath(os.path.normcase(path))
862 if normpath not in normpaths:
863 result.append(path)
864 normpaths.append(normpath)
865 newpaths.reverse()
866 for path in newpaths:
867 if not path:
868 continue
869 normpath = os.path.normpath(os.path.normcase(path))
870 if normpath not in normpaths:
871 result.insert(0, path)
872 normpaths.append(normpath)
873 paths = result
874
875 else:
876 newpaths = newpaths + paths
877
878 normpaths = []
879 paths = []
880
881 for path in newpaths:
882 normpath = os.path.normpath(os.path.normcase(path))
883 if path and not normpath in normpaths:
884 paths.append(path)
885 normpaths.append(normpath)
886
887 if is_list:
888 return paths
889 else:
890 return sep.join(paths)
891
892 -def AppendPath(oldpath, newpath, sep = os.pathsep,
893 delete_existing=1, canonicalize=None):
894 """This appends new path elements to the given old path. Will
895 only add any particular path once (leaving the last one it
896 encounters and ignoring the rest, to preserve path order), and
897 will os.path.normpath and os.path.normcase all paths to help
898 assure this. This can also handle the case where the given old
899 path variable is a list instead of a string, in which case a list
900 will be returned instead of a string.
901
902 Example:
903 Old Path: "/foo/bar:/foo"
904 New Path: "/biz/boom:/foo"
905 Result: "/foo/bar:/biz/boom:/foo"
906
907 If delete_existing is 0, then adding a path that exists
908 will not move it to the end; it will stay where it is in the list.
909
910 If canonicalize is not None, it is applied to each element of
911 newpath before use.
912 """
913
914 orig = oldpath
915 is_list = 1
916 paths = orig
917 if not is_List(orig) and not is_Tuple(orig):
918 paths = paths.split(sep)
919 is_list = 0
920
921 if is_String(newpath):
922 newpaths = newpath.split(sep)
923 elif not is_List(newpath) and not is_Tuple(newpath):
924 newpaths = [ newpath ]
925 else:
926 newpaths = newpath
927
928 if canonicalize:
929 newpaths=list(map(canonicalize, newpaths))
930
931 if not delete_existing:
932
933
934
935
936
937 result = []
938 normpaths = []
939 for path in paths:
940 if not path:
941 continue
942 result.append(path)
943 normpaths.append(os.path.normpath(os.path.normcase(path)))
944 for path in newpaths:
945 if not path:
946 continue
947 normpath = os.path.normpath(os.path.normcase(path))
948 if normpath not in normpaths:
949 result.append(path)
950 normpaths.append(normpath)
951 paths = result
952 else:
953
954
955 newpaths = paths + newpaths
956 newpaths.reverse()
957
958 normpaths = []
959 paths = []
960
961 for path in newpaths:
962 normpath = os.path.normpath(os.path.normcase(path))
963 if path and not normpath in normpaths:
964 paths.append(path)
965 normpaths.append(normpath)
966 paths.reverse()
967
968 if is_list:
969 return paths
970 else:
971 return sep.join(paths)
972
974 """This function will take 'key' out of the dictionary
975 'env_dict', then add the path 'path' to that key if it is not
976 already there. This treats the value of env_dict[key] as if it
977 has a similar format to the PATH variable...a list of paths
978 separated by tokens. The 'path' will get added to the list if it
979 is not already there."""
980 try:
981 is_list = 1
982 paths = env_dict[key]
983 if not is_List(env_dict[key]):
984 paths = paths.split(sep)
985 is_list = 0
986 if os.path.normcase(path) not in list(map(os.path.normcase, paths)):
987 paths = [ path ] + paths
988 if is_list:
989 env_dict[key] = paths
990 else:
991 env_dict[key] = sep.join(paths)
992 except KeyError:
993 env_dict[key] = path
994
995 if sys.platform == 'cygwin':
997 """Transforms an absolute path into a native path for the system. In
998 Cygwin, this converts from a Cygwin path to a Windows one."""
999 return os.popen('cygpath -w ' + path).read().replace('\n', '')
1000 else:
1002 """Transforms an absolute path into a native path for the system.
1003 Non-Cygwin version, just leave the path alone."""
1004 return path
1005
1006 display = DisplayEngine()
1007
1009 if is_List(arg) or is_Tuple(arg):
1010 return arg
1011 elif is_String(arg):
1012 return arg.split()
1013 else:
1014 return [arg]
1015
1017 """A class for command-line construction variables.
1018
1019 This is a list that uses Split() to split an initial string along
1020 white-space arguments, and similarly to split any strings that get
1021 added. This allows us to Do the Right Thing with Append() and
1022 Prepend() (as well as straight Python foo = env['VAR'] + 'arg1
1023 arg2') regardless of whether a user adds a list or a string to a
1024 command-line construction variable.
1025 """
1033 return (self, CLVar(other))
1035 return ' '.join(self.data)
1036
1037
1038
1039
1040
1043 self._keys = []
1044 UserDict.__init__(self, dict)
1045
1049
1053
1055 UserDict.clear(self)
1056 self._keys = []
1057
1062
1064 return list(zip(self._keys, list(self.values())))
1065
1067 return self._keys[:]
1068
1070 try:
1071 key = self._keys[-1]
1072 except IndexError:
1073 raise KeyError('dictionary is empty')
1074
1075 val = self[key]
1076 del self[key]
1077
1078 return (key, val)
1079
1083
1087
1089 return list(map(self.get, self._keys))
1090
1092 """A callable ordered dictionary that maps file suffixes to
1093 dictionary values. We preserve the order in which items are added
1094 so that get_suffix() calls always return the first suffix added."""
1095 - def __call__(self, env, source, ext=None):
1096 if ext is None:
1097 try:
1098 ext = source[0].get_suffix()
1099 except IndexError:
1100 ext = ""
1101 try:
1102 return self[ext]
1103 except KeyError:
1104
1105
1106 s_dict = {}
1107 for (k,v) in self.items():
1108 if k is not None:
1109 s_k = env.subst(k)
1110 if s_k in s_dict:
1111
1112
1113
1114
1115 raise KeyError(s_dict[s_k][0], k, s_k)
1116 s_dict[s_k] = (k,v)
1117 try:
1118 return s_dict[ext][1]
1119 except KeyError:
1120 try:
1121 return self[None]
1122 except KeyError:
1123 return None
1124
1125
1126 if sys.platform == 'cygwin':
1127
1128
1131 else:
1133 return (os.path.normcase(s1) != os.path.normcase(s2))
1134
1135 -def adjustixes(fname, pre, suf, ensure_suffix=False):
1136 if pre:
1137 path, fn = os.path.split(os.path.normpath(fname))
1138 if fn[:len(pre)] != pre:
1139 fname = os.path.join(path, pre + fn)
1140
1141
1142
1143 if suf and fname[-len(suf):] != suf and \
1144 (ensure_suffix or not splitext(fname)[1]):
1145 fname = fname + suf
1146 return fname
1147
1148
1149
1150
1151
1152
1153
1154
1156 """Return a list of the elements in s, but without duplicates.
1157
1158 For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
1159 unique("abcabc") some permutation of ["a", "b", "c"], and
1160 unique(([1, 2], [2, 3], [1, 2])) some permutation of
1161 [[2, 3], [1, 2]].
1162
1163 For best speed, all sequence elements should be hashable. Then
1164 unique() will usually work in linear time.
1165
1166 If not possible, the sequence elements should enjoy a total
1167 ordering, and if list(s).sort() doesn't raise TypeError it's
1168 assumed that they do enjoy a total ordering. Then unique() will
1169 usually work in O(N*log2(N)) time.
1170
1171 If that's not possible either, the sequence elements must support
1172 equality-testing. Then unique() will usually work in quadratic
1173 time.
1174 """
1175
1176 n = len(s)
1177 if n == 0:
1178 return []
1179
1180
1181
1182
1183
1184 u = {}
1185 try:
1186 for x in s:
1187 u[x] = 1
1188 except TypeError:
1189 pass
1190 else:
1191 return list(u.keys())
1192 del u
1193
1194
1195
1196
1197
1198
1199
1200
1201 try:
1202 t = sorted(s)
1203 except TypeError:
1204 pass
1205 else:
1206 assert n > 0
1207 last = t[0]
1208 lasti = i = 1
1209 while i < n:
1210 if t[i] != last:
1211 t[lasti] = last = t[i]
1212 lasti = lasti + 1
1213 i = i + 1
1214 return t[:lasti]
1215 del t
1216
1217
1218 u = []
1219 for x in s:
1220 if x not in u:
1221 u.append(x)
1222 return u
1223
1224
1225
1226
1227
1228
1229
1230
1231
1233 if idfun is None:
1234 def idfun(x): return x
1235 seen = {}
1236 result = []
1237 for item in seq:
1238 marker = idfun(item)
1239
1240
1241
1242 if marker in seen: continue
1243 seen[marker] = 1
1244 result.append(item)
1245 return result
1246
1247
1248
1249
1250
1252 seen = {}
1253 result = []
1254 for item in seq:
1255
1256 if item not in seen:
1257 seen[item] = 1
1258 result.append(item)
1259 return result
1260
1261
1262
1263
1265 logical_line = []
1266 for line in physical_lines:
1267 stripped = line.rstrip()
1268 if stripped.endswith('\\'):
1269
1270 logical_line.append(stripped[:-1])
1271 else:
1272
1273 logical_line.append(line)
1274 yield joiner(logical_line)
1275 logical_line = []
1276 if logical_line:
1277
1278 yield joiner(logical_line)
1279
1280
1282 """ Wrapper class for the logical_lines method.
1283
1284 Allows us to read all "logical" lines at once from a
1285 given file object.
1286 """
1287
1289 self.fileobj = fileobj
1290
1292 result = [l for l in logical_lines(self.fileobj)]
1293 return result
1294
1295
1298 UserList.__init__(self, seq)
1299 self.unique = True
1301 if not self.unique:
1302 self.data = uniquer_hashables(self.data)
1303 self.unique = True
1332 UserList.__setitem__(self, i, item)
1333 self.unique = False
1338 UserList.__setslice__(self, i, j, other)
1339 self.unique = False
1365 UserList.append(self, item)
1366 self.unique = False
1368 UserList.insert(self, i)
1369 self.unique = False
1379 - def sort(self, *args, **kwds):
1383 UserList.extend(self, other)
1384 self.unique = False
1385
1386
1388 """
1389 A proxy class that wraps a file object, flushing after every write,
1390 and delegating everything else to the wrapped object.
1391 """
1393 self.file = file
1394 self.softspace = 0
1396 try:
1397 self.file.write(arg)
1398 self.file.flush()
1399 except IOError:
1400
1401
1402
1403
1404
1405
1406
1407 pass
1409 return getattr(self.file, attr)
1410
1412 """ makes an absolute path name to a relative pathname.
1413 """
1414 if os.path.isabs(path):
1415 drive_s,path = os.path.splitdrive(path)
1416
1417 import re
1418 if not drive_s:
1419 path=re.compile("/*(.*)").findall(path)[0]
1420 else:
1421 path=path[1:]
1422
1423 assert( not os.path.isabs( path ) ), path
1424 return path
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1453 """
1454 Adds either a bound method to an instance or the function itself (or an unbound method in Python 2) to a class.
1455 If name is ommited the name of the specified function
1456 is used by default.
1457
1458 Example::
1459
1460 a = A()
1461 def f(self, x, y):
1462 self.z = x + y
1463 AddMethod(f, A, "add")
1464 a.add(2, 4)
1465 print(a.z)
1466 AddMethod(lambda self, i: self.l[i], a, "listIndex")
1467 print(a.listIndex(5))
1468 """
1469 if name is None:
1470 name = function.__name__
1471 else:
1472 function = RenameFunction(function, name)
1473
1474
1475
1476 if hasattr(obj, '__class__') and obj.__class__ is not type:
1477
1478 if sys.version_info[:2] > (3, 2):
1479 method = MethodType(function, obj)
1480 else:
1481 method = MethodType(function, obj, obj.__class__)
1482 else:
1483
1484 method = function
1485
1486 setattr(obj, name, method)
1487
1489 """
1490 Returns a function identical to the specified function, but with
1491 the specified name.
1492 """
1493 return FunctionType(function.__code__,
1494 function.__globals__,
1495 name,
1496 function.__defaults__)
1497
1498
1499 md5 = False
1500
1501
1504
1505
1510
1511 try:
1512 import hashlib
1513 except ImportError:
1514 pass
1515 else:
1516 if hasattr(hashlib, 'md5'):
1517 md5 = True
1518
1520 m = hashlib.md5()
1521
1522 try:
1523 m.update(to_bytes(s))
1524 except TypeError as e:
1525 m.update(to_bytes(str(s)))
1526
1527 return m.hexdigest()
1528
1530 m = hashlib.md5()
1531 f = open(fname, "rb")
1532 while True:
1533 blck = f.read(chunksize)
1534 if not blck:
1535 break
1536 m.update(to_bytes(blck))
1537 f.close()
1538 return m.hexdigest()
1539
1541 """
1542 Collects a list of signatures into an aggregate signature.
1543
1544 signatures - a list of signatures
1545 returns - the aggregate signature
1546 """
1547 if len(signatures) == 1:
1548 return signatures[0]
1549 else:
1550 return MD5signature(', '.join(signatures))
1551
1552
1553
1555 """
1556 Perform sys.intern() on the passed argument and return the result.
1557 If the input is ineligible (e.g. a unicode string) the original argument is
1558 returned and no exception is thrown.
1559 """
1560 try:
1561 return sys.intern(x)
1562 except TypeError:
1563 return x
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574 -class Null(object):
1575 """ Null objects always and reliably "do nothing." """
1576 - def __new__(cls, *args, **kwargs):
1585 return "Null(0x%08X)" % id(self)
1596
1608
1609
1610 del __revision__
1611
1613 if isinstance (s, (bytes, bytearray)) or bytes is str:
1614 return s
1615 return bytes (s, 'utf-8')
1616
1618 if bytes is str or is_String(s):
1619 return s
1620 return str (s, 'utf-8')
1621
1622
1623
1624
1626 """
1627 Define cmp because it's no longer available in python3
1628 Works under python 2 as well
1629 """
1630 return (a > b) - (a < b)
1631
1632
1633
1634
1635
1636
1637
1638