|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing classes and functions to dump variable contents. |
|
8 """ |
|
9 |
|
10 # |
|
11 # This code was inspired by pydevd. |
|
12 # |
|
13 |
|
14 MaxItemsToHandle = 300 |
|
15 TooLargeMessage = ("Too large to show contents. Max items to show: " + |
|
16 str(MaxItemsToHandle)) |
|
17 TooLargeAttribute = "Too large to be handled." |
|
18 |
|
19 ############################################################ |
|
20 ## Classes implementing resolvers for various compund types |
|
21 ############################################################ |
|
22 |
|
23 |
|
24 class BaseResolver(object): |
|
25 """ |
|
26 Base class of the resolver class tree. |
|
27 """ |
|
28 def resolve(self, var, attribute): |
|
29 """ |
|
30 Public method to get an attribute from a variable. |
|
31 |
|
32 @param var variable to extract an attribute or value from |
|
33 @type any |
|
34 @param attribute name of the attribute to extract |
|
35 @type str |
|
36 @return value of the attribute |
|
37 @rtype any |
|
38 @exception NotImplementedError raised to indicate a missing |
|
39 implementation |
|
40 """ # __IGNORE_WARNING_D235__ |
|
41 raise NotImplementedError |
|
42 |
|
43 def getDictionary(self, var): |
|
44 """ |
|
45 Public method to get the attributes of a variable as a dictionary. |
|
46 |
|
47 @param var variable to be converted |
|
48 @type any |
|
49 @return dictionary containing the variable attributes |
|
50 @rtype dict |
|
51 @exception NotImplementedError raised to indicate a missing |
|
52 implementation |
|
53 """ # __IGNORE_WARNING_D235__ |
|
54 raise NotImplementedError |
|
55 |
|
56 |
|
57 class DefaultResolver(BaseResolver): |
|
58 """ |
|
59 Class used to resolve the default way. |
|
60 """ |
|
61 def resolve(self, var, attribute): |
|
62 """ |
|
63 Public method to get an attribute from a variable. |
|
64 |
|
65 @param var variable to extract an attribute or value from |
|
66 @type any |
|
67 @param attribute name of the attribute to extract |
|
68 @type str |
|
69 @return value of the attribute |
|
70 @rtype any |
|
71 """ |
|
72 return getattr(var, attribute) |
|
73 |
|
74 def getDictionary(self, var): |
|
75 """ |
|
76 Public method to get the attributes of a variable as a dictionary. |
|
77 |
|
78 @param var variable to be converted |
|
79 @type any |
|
80 @return dictionary containing the variable attributes |
|
81 @rtype dict |
|
82 """ |
|
83 names = dir(var) |
|
84 if not names and hasattr(var, "__members__"): |
|
85 names = var.__members__ |
|
86 |
|
87 d = {} |
|
88 for name in names: |
|
89 try: |
|
90 attribute = getattr(var, name) |
|
91 d[name] = attribute |
|
92 except Exception: |
|
93 pass # if we can't get it, simply ignore it |
|
94 |
|
95 return d |
|
96 |
|
97 |
|
98 class DictResolver(BaseResolver): |
|
99 """ |
|
100 Class used to resolve from a dictionary. |
|
101 """ |
|
102 def resolve(self, var, attribute): |
|
103 """ |
|
104 Public method to get an attribute from a variable. |
|
105 |
|
106 @param var variable to extract an attribute or value from |
|
107 @type dict |
|
108 @param attribute name of the attribute to extract |
|
109 @type str |
|
110 @return value of the attribute |
|
111 @rtype any |
|
112 """ |
|
113 if attribute in ('___len___', TooLargeAttribute): |
|
114 return None |
|
115 |
|
116 if "(ID:" not in attribute: |
|
117 try: |
|
118 return var[attribute] |
|
119 except Exception: |
|
120 return getattr(var, attribute) |
|
121 |
|
122 expectedID = int(attribute.split("(ID:")[-1][:-1]) |
|
123 for key, value in var.items(): |
|
124 if id(key) == expectedID: |
|
125 return value |
|
126 |
|
127 return None |
|
128 |
|
129 def __keyToStr(self, key): |
|
130 """ |
|
131 Private method to get a string representation for a key. |
|
132 |
|
133 @param key key to be converted |
|
134 @type any |
|
135 @return string representation of the given key |
|
136 @rtype str |
|
137 """ |
|
138 if isinstance(key, str): |
|
139 return repr(key) |
|
140 else: |
|
141 return key |
|
142 |
|
143 def getDictionary(self, var): |
|
144 """ |
|
145 Public method to get the attributes of a variable as a dictionary. |
|
146 |
|
147 @param var variable to be converted |
|
148 @type any |
|
149 @return dictionary containing the variable attributes |
|
150 @rtype dict |
|
151 """ |
|
152 d = {} |
|
153 count = 0 |
|
154 for key, value in var.items(): |
|
155 count += 1 |
|
156 key = "{0} (ID:{1})".format(self.__keyToStr(key), id(key)) |
|
157 d[key] = value |
|
158 if count > MaxItemsToHandle: |
|
159 d[TooLargeAttribute] = TooLargeMessage |
|
160 break |
|
161 |
|
162 d["___len___"] = len(var) |
|
163 |
|
164 # in case it has additional fields |
|
165 additionals = defaultResolver.getDictionary(var) |
|
166 d.update(additionals) |
|
167 |
|
168 return d |
|
169 |
|
170 |
|
171 class ListResolver(BaseResolver): |
|
172 """ |
|
173 Class used to resolve from a tuple or list. |
|
174 """ |
|
175 def resolve(self, var, attribute): |
|
176 """ |
|
177 Public method to get an attribute from a variable. |
|
178 |
|
179 @param var variable to extract an attribute or value from |
|
180 @type tuple or list |
|
181 @param attribute name of the attribute to extract |
|
182 @type str |
|
183 @return value of the attribute |
|
184 @rtype any |
|
185 """ |
|
186 if attribute in ('___len___', TooLargeAttribute): |
|
187 return None |
|
188 |
|
189 try: |
|
190 return var[int(attribute)] |
|
191 except Exception: |
|
192 return getattr(var, attribute) |
|
193 |
|
194 def getDictionary(self, var): |
|
195 """ |
|
196 Public method to get the attributes of a variable as a dictionary. |
|
197 |
|
198 @param var variable to be converted |
|
199 @type any |
|
200 @return dictionary containing the variable attributes |
|
201 @rtype dict |
|
202 """ |
|
203 d = {} |
|
204 count = 0 |
|
205 for value in var: |
|
206 d[str(count)] = value |
|
207 count += 1 |
|
208 if count > MaxItemsToHandle: |
|
209 d[TooLargeAttribute] = TooLargeMessage |
|
210 break |
|
211 |
|
212 d["___len___"] = len(var) |
|
213 |
|
214 # in case it has additional fields |
|
215 additionals = defaultResolver.getDictionary(var) |
|
216 d.update(additionals) |
|
217 |
|
218 return d |
|
219 |
|
220 |
|
221 class SetResolver(BaseResolver): |
|
222 """ |
|
223 Class used to resolve from a set or frozenset. |
|
224 """ |
|
225 def resolve(self, var, attribute): |
|
226 """ |
|
227 Public method to get an attribute from a variable. |
|
228 |
|
229 @param var variable to extract an attribute or value from |
|
230 @type tuple or list |
|
231 @param attribute id of the value to extract |
|
232 @type str |
|
233 @return value of the attribute |
|
234 @rtype any |
|
235 """ |
|
236 if attribute in ('___len___', TooLargeAttribute): |
|
237 return None |
|
238 |
|
239 if attribute.startswith("ID:"): |
|
240 attribute = attribute.split(None, 1)[1] |
|
241 try: |
|
242 attribute = int(attribute) |
|
243 except Exception: |
|
244 return getattr(var, attribute) |
|
245 |
|
246 for v in var: |
|
247 if id(v) == attribute: |
|
248 return v |
|
249 |
|
250 return None |
|
251 |
|
252 def getDictionary(self, var): |
|
253 """ |
|
254 Public method to get the attributes of a variable as a dictionary. |
|
255 |
|
256 @param var variable to be converted |
|
257 @type any |
|
258 @return dictionary containing the variable attributes |
|
259 @rtype dict |
|
260 """ |
|
261 d = {} |
|
262 count = 0 |
|
263 for value in var: |
|
264 count += 1 |
|
265 d["ID: " + str(id(value))] = value |
|
266 if count > MaxItemsToHandle: |
|
267 d[TooLargeAttribute] = TooLargeMessage |
|
268 break |
|
269 |
|
270 d["___len___"] = len(var) |
|
271 |
|
272 # in case it has additional fields |
|
273 additionals = defaultResolver.getDictionary(var) |
|
274 d.update(additionals) |
|
275 |
|
276 return d |
|
277 |
|
278 |
|
279 defaultResolver = DefaultResolver() |
|
280 dictResolver = DictResolver() |
|
281 listResolver = ListResolver() |
|
282 setResolver = SetResolver() |
|
283 |
|
284 # TODO: add resolver for numpy arrays |
|
285 # TODO: add resolver for Django MultiValueDict |
|
286 # TODO: add resolver for collections.deque |
|
287 |
|
288 ############################################################ |
|
289 ## Methods to determine the type of a variable and the |
|
290 ## resolver class to use |
|
291 ############################################################ |
|
292 |
|
293 _TypeMap = None |
|
294 |
|
295 |
|
296 def _initTypeMap(): |
|
297 """ |
|
298 Protected function to initialize the type map. |
|
299 """ |
|
300 global _TypeMap |
|
301 |
|
302 _TypeMap = [ |
|
303 (type(None), None,), |
|
304 (int, None), |
|
305 (float, None), |
|
306 (complex, None), |
|
307 (str, None), |
|
308 (tuple, listResolver), |
|
309 (list, listResolver), |
|
310 (dict, dictResolver), |
|
311 ] |
|
312 |
|
313 try: |
|
314 _TypeMap.append((long, None)) # __IGNORE_WARNING__ |
|
315 except Exception: |
|
316 pass # not available on all python versions |
|
317 |
|
318 try: |
|
319 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__ |
|
320 except Exception: |
|
321 pass # not available on all python versions |
|
322 |
|
323 try: |
|
324 _TypeMap.append((set, setResolver)) # __IGNORE_WARNING__ |
|
325 except Exception: |
|
326 pass # not available on all python versions |
|
327 |
|
328 try: |
|
329 _TypeMap.append((frozenset, setResolver)) # __IGNORE_WARNING__ |
|
330 except Exception: |
|
331 pass # not available on all python versions |
|
332 |
|
333 |
|
334 def getType(obj): |
|
335 """ |
|
336 Public method to get the type information for an object. |
|
337 |
|
338 @param obj object to get type information for |
|
339 @type any |
|
340 @return tuple containing the type, type name, type string and resolver |
|
341 @rtype tuple of type, str, str, BaseResolver |
|
342 """ |
|
343 typeObject = type(obj) |
|
344 typeName = typeObject.__name__ |
|
345 typeStr = str(typeObject)[8:-2] |
|
346 |
|
347 if typeStr.startswith(("PyQt5.", "PyQt4.")): |
|
348 resolver = None |
|
349 else: |
|
350 if _TypeMap is None: |
|
351 _initTypeMap() |
|
352 |
|
353 for typeData in _TypeMap: |
|
354 if isinstance(obj, typeData[0]): |
|
355 resolver = typeData[1] |
|
356 break |
|
357 else: |
|
358 resolver = defaultResolver |
|
359 |
|
360 return typeObject, typeName, typeStr, resolver |
|
361 |
|
362 # |
|
363 # eflag: noqa = M702 |