|
86
|
1 # Copyright (C) 2008 Canonical Ltd |
|
|
2 # |
|
|
3 # This program is free software; you can redistribute it and/or modify |
|
|
4 # it under the terms of the GNU General Public License as published by |
|
|
5 # the Free Software Foundation; either version 2 of the License, or |
|
|
6 # (at your option) any later version. |
|
|
7 # |
|
|
8 # This program is distributed in the hope that it will be useful, |
|
|
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
11 # GNU General Public License for more details. |
|
|
12 # |
|
|
13 # You should have received a copy of the GNU General Public License |
|
|
14 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
15 |
|
|
16 """Miscellaneous useful stuff.""" |
|
|
17 import sys |
|
|
18 |
|
|
19 |
|
|
20 def _common_path_and_rest(l1, l2, common=[]): |
|
|
21 # From http://code.activestate.com/recipes/208993/ |
|
|
22 if len(l1) < 1: return (common, l1, l2) |
|
|
23 if len(l2) < 1: return (common, l1, l2) |
|
|
24 if l1[0] != l2[0]: return (common, l1, l2) |
|
|
25 return _common_path_and_rest( |
|
|
26 l1[1:], |
|
|
27 l2[1:], |
|
|
28 common + [ |
|
|
29 l1[0:1] # return a byte string in python 3 unlike l1[0] that |
|
|
30 # would return an integer. |
|
|
31 ] |
|
|
32 ) |
|
|
33 |
|
|
34 |
|
|
35 def common_path(path1, path2): |
|
|
36 """Find the common bit of 2 paths.""" |
|
|
37 return b''.join(_common_path_and_rest(path1, path2)[0]) |
|
|
38 |
|
|
39 |
|
|
40 def common_directory(paths): |
|
|
41 """Find the deepest common directory of a list of paths. |
|
|
42 |
|
|
43 :return: if no paths are provided, None is returned; |
|
|
44 if there is no common directory, '' is returned; |
|
|
45 otherwise the common directory with a trailing / is returned. |
|
|
46 """ |
|
|
47 import posixpath |
|
|
48 def get_dir_with_slash(path): |
|
|
49 if path == b'' or path.endswith(b'/'): |
|
|
50 return path |
|
|
51 else: |
|
|
52 dirname, basename = posixpath.split(path) |
|
|
53 if dirname == b'': |
|
|
54 return dirname |
|
|
55 else: |
|
|
56 return dirname + b'/' |
|
|
57 |
|
|
58 if not paths: |
|
|
59 return None |
|
|
60 elif len(paths) == 1: |
|
|
61 return get_dir_with_slash(paths[0]) |
|
|
62 else: |
|
|
63 common = common_path(paths[0], paths[1]) |
|
|
64 for path in paths[2:]: |
|
|
65 common = common_path(common, path) |
|
|
66 return get_dir_with_slash(common) |
|
|
67 |
|
|
68 |
|
|
69 def is_inside(directory, fname): |
|
|
70 """True if fname is inside directory. |
|
|
71 |
|
|
72 The parameters should typically be passed to osutils.normpath first, so |
|
|
73 that . and .. and repeated slashes are eliminated, and the separators |
|
|
74 are canonical for the platform. |
|
|
75 |
|
|
76 The empty string as a dir name is taken as top-of-tree and matches |
|
|
77 everything. |
|
|
78 """ |
|
|
79 # XXX: Most callers of this can actually do something smarter by |
|
|
80 # looking at the inventory |
|
|
81 if directory == fname: |
|
|
82 return True |
|
|
83 |
|
|
84 if directory == b'': |
|
|
85 return True |
|
|
86 |
|
|
87 if not directory.endswith(b'/'): |
|
|
88 directory += b'/' |
|
|
89 |
|
|
90 return fname.startswith(directory) |
|
|
91 |
|
|
92 |
|
|
93 def is_inside_any(dir_list, fname): |
|
|
94 """True if fname is inside any of given dirs.""" |
|
|
95 for dirname in dir_list: |
|
|
96 if is_inside(dirname, fname): |
|
|
97 return True |
|
|
98 return False |
|
|
99 |
|
|
100 |
|
|
101 def utf8_bytes_string(s): |
|
|
102 """Convert a string to a bytes string (if necessary, encode in utf8)""" |
|
|
103 if sys.version_info[0] == 2: |
|
|
104 if isinstance(s, str): |
|
|
105 return s |
|
|
106 else: |
|
|
107 return s.encode('utf8') |
|
|
108 else: |
|
|
109 if isinstance(s, str): |
|
|
110 return bytes(s, encoding='utf8') |
|
|
111 else: |
|
|
112 return s |
|
|
113 |
|
|
114 |
|
|
115 def repr_bytes(obj): |
|
|
116 """Return a bytes representation of the object""" |
|
|
117 if sys.version_info[0] == 2: |
|
|
118 return repr(obj) |
|
|
119 else: |
|
|
120 return bytes(obj) |
|
|
121 |
|
|
122 |
|
|
123 class newobject(object): |
|
|
124 """ |
|
|
125 A magical object class that provides Python 2 compatibility methods:: |
|
|
126 next |
|
|
127 __unicode__ |
|
|
128 __nonzero__ |
|
|
129 |
|
|
130 Subclasses of this class can merely define the Python 3 methods (__next__, |
|
|
131 __str__, and __bool__). |
|
|
132 |
|
|
133 This is a copy/paste of the future.types.newobject class of the future |
|
|
134 package. |
|
|
135 """ |
|
|
136 def next(self): |
|
|
137 if hasattr(self, '__next__'): |
|
|
138 return type(self).__next__(self) |
|
|
139 raise TypeError('newobject is not an iterator') |
|
|
140 |
|
|
141 def __unicode__(self): |
|
|
142 # All subclasses of the builtin object should have __str__ defined. |
|
|
143 # Note that old-style classes do not have __str__ defined. |
|
|
144 if hasattr(self, '__str__'): |
|
|
145 s = type(self).__str__(self) |
|
|
146 else: |
|
|
147 s = str(self) |
|
|
148 if isinstance(s, unicode): |
|
|
149 return s |
|
|
150 else: |
|
|
151 return s.decode('utf-8') |
|
|
152 |
|
|
153 def __nonzero__(self): |
|
|
154 if hasattr(self, '__bool__'): |
|
|
155 return type(self).__bool__(self) |
|
|
156 # object has no __nonzero__ method |
|
|
157 return True |
|
|
158 |
|
|
159 # Are these ever needed? |
|
|
160 # def __div__(self): |
|
|
161 # return self.__truediv__() |
|
|
162 |
|
|
163 # def __idiv__(self, other): |
|
|
164 # return self.__itruediv__(other) |
|
|
165 |
|
|
166 def __long__(self): |
|
|
167 if not hasattr(self, '__int__'): |
|
|
168 return NotImplemented |
|
|
169 return self.__int__() # not type(self).__int__(self) |
|
|
170 |
|
|
171 # def __new__(cls, *args, **kwargs): |
|
|
172 # """ |
|
|
173 # dict() -> new empty dictionary |
|
|
174 # dict(mapping) -> new dictionary initialized from a mapping object's |
|
|
175 # (key, value) pairs |
|
|
176 # dict(iterable) -> new dictionary initialized as if via: |
|
|
177 # d = {} |
|
|
178 # for k, v in iterable: |
|
|
179 # d[k] = v |
|
|
180 # dict(**kwargs) -> new dictionary initialized with the name=value pairs |
|
|
181 # in the keyword argument list. For example: dict(one=1, two=2) |
|
|
182 # """ |
|
|
183 |
|
|
184 # if len(args) == 0: |
|
|
185 # return super(newdict, cls).__new__(cls) |
|
|
186 # elif type(args[0]) == newdict: |
|
|
187 # return args[0] |
|
|
188 # else: |
|
|
189 # value = args[0] |
|
|
190 # return super(newdict, cls).__new__(cls, value) |
|
|
191 |
|
|
192 def __native__(self): |
|
|
193 """ |
|
|
194 Hook for the future.utils.native() function |
|
|
195 """ |
|
|
196 return object(self) |
|
|
197 |
|
|
198 |
|
|
199 def binary_stream(stream): |
|
|
200 """Ensure a stream is binary on Windows. |
|
|
201 |
|
|
202 :return: the stream |
|
|
203 """ |
|
|
204 try: |
|
|
205 import os |
|
|
206 if os.name == 'nt': |
|
|
207 fileno = getattr(stream, 'fileno', None) |
|
|
208 if fileno: |
|
|
209 no = fileno() |
|
|
210 if no >= 0: # -1 means we're working as subprocess |
|
|
211 import msvcrt |
|
|
212 msvcrt.setmode(no, os.O_BINARY) |
|
|
213 except ImportError: |
|
|
214 pass |
|
|
215 return stream |
|
|
216 |
|
|
217 |
|
|
218 def invert_dictset(d): |
|
|
219 """Invert a dictionary with keys matching a set of values, turned into lists.""" |
|
|
220 # Based on recipe from ASPN |
|
|
221 result = {} |
|
|
222 for k, c in d.items(): |
|
|
223 for v in c: |
|
|
224 keys = result.setdefault(v, []) |
|
|
225 keys.append(k) |
|
|
226 return result |
|
|
227 |
|
|
228 |
|
|
229 def invert_dict(d): |
|
|
230 """Invert a dictionary with keys matching each value turned into a list.""" |
|
|
231 # Based on recipe from ASPN |
|
|
232 result = {} |
|
|
233 for k, v in d.items(): |
|
|
234 keys = result.setdefault(v, []) |
|
|
235 keys.append(k) |
|
|
236 return result |
|
|
237 |
|
|
238 |
|
|
239 def defines_to_dict(defines): |
|
|
240 """Convert a list of definition strings to a dictionary.""" |
|
|
241 if defines is None: |
|
|
242 return None |
|
|
243 result = {} |
|
|
244 for define in defines: |
|
|
245 kv = define.split('=', 1) |
|
|
246 if len(kv) == 1: |
|
|
247 result[define.strip()] = 1 |
|
|
248 else: |
|
|
249 result[kv[0].strip()] = kv[1].strip() |
|
|
250 return result |
|
|
251 |
|
|
252 |
|
|
253 def get_source_stream(source): |
|
|
254 if source == '-' or source is None: |
|
|
255 import sys |
|
|
256 stream = binary_stream(sys.stdin) |
|
|
257 elif source.endswith('.gz'): |
|
|
258 import gzip |
|
|
259 stream = gzip.open(source, "rb") |
|
|
260 else: |
|
|
261 stream = open(source, "rb") |
|
|
262 return stream |
|
|
263 |
|
|
264 |
|
|
265 |