DebugClients/Python2/DebugVariables.py

changeset 5241
883e7084818d
parent 5238
682d0f6a6074
parent 5240
71c51aae2f4e
child 5243
f6346f4b46c2
equal deleted inserted replaced
5238:682d0f6a6074 5241:883e7084818d
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 ############################################################
58 ## Default Resolver
59 ############################################################
60
61
62 class DefaultResolver(BaseResolver):
63 """
64 Class used to resolve the default way.
65 """
66 def resolve(self, var, attribute):
67 """
68 Public method to get an attribute from a variable.
69
70 @param var variable to extract an attribute or value from
71 @type any
72 @param attribute name of the attribute to extract
73 @type str
74 @return value of the attribute
75 @rtype any
76 """
77 return getattr(var, attribute, None)
78
79 def getDictionary(self, var):
80 """
81 Public method to get the attributes of a variable as a dictionary.
82
83 @param var variable to be converted
84 @type any
85 @return dictionary containing the variable attributes
86 @rtype dict
87 """
88 names = dir(var)
89 if not names and hasattr(var, "__members__"):
90 names = var.__members__
91
92 d = {}
93 for name in names:
94 try:
95 attribute = getattr(var, name)
96 d[name] = attribute
97 except Exception:
98 pass # if we can't get it, simply ignore it
99
100 return d
101
102
103 ############################################################
104 ## Resolver for Dictionaries
105 ############################################################
106
107
108 class DictResolver(BaseResolver):
109 """
110 Class used to resolve from a dictionary.
111 """
112 def resolve(self, var, attribute):
113 """
114 Public method to get an attribute from a variable.
115
116 @param var variable to extract an attribute or value from
117 @type dict
118 @param attribute name of the attribute to extract
119 @type str
120 @return value of the attribute
121 @rtype any
122 """
123 if attribute in ('___len___', TooLargeAttribute):
124 return None
125
126 if "(ID:" not in attribute:
127 try:
128 return var[attribute]
129 except Exception:
130 return getattr(var, attribute, None)
131
132 expectedID = int(attribute.split("(ID:")[-1][:-1])
133 for key, value in var.items():
134 if id(key) == expectedID:
135 return value
136
137 return None
138
139 def keyToStr(self, key):
140 """
141 Public method to get a string representation for a key.
142
143 @param key key to be converted
144 @type any
145 @return string representation of the given key
146 @rtype str
147 """
148 if isinstance(key, str):
149 return repr(key)
150 else:
151 return key
152
153 def getDictionary(self, var):
154 """
155 Public method to get the attributes of a variable as a dictionary.
156
157 @param var variable to be converted
158 @type any
159 @return dictionary containing the variable attributes
160 @rtype dict
161 """
162 d = {}
163 count = 0
164 for key, value in var.items():
165 count += 1
166 key = "%s (ID:%s)" % (self.keyToStr(key), id(key))
167 d[key] = value
168 if count > MaxItemsToHandle:
169 d[TooLargeAttribute] = TooLargeMessage
170 break
171
172 d["___len___"] = len(var)
173
174 # in case it has additional fields
175 additionals = defaultResolver.getDictionary(var)
176 d.update(additionals)
177
178 return d
179
180
181 ############################################################
182 ## Resolver for Lists and Tuples
183 ############################################################
184
185
186 class ListResolver(BaseResolver):
187 """
188 Class used to resolve from a tuple or list.
189 """
190 def resolve(self, var, attribute):
191 """
192 Public method to get an attribute from a variable.
193
194 @param var variable to extract an attribute or value from
195 @type tuple or list
196 @param attribute name of the attribute to extract
197 @type str
198 @return value of the attribute
199 @rtype any
200 """
201 if attribute in ('___len___', TooLargeAttribute):
202 return None
203
204 try:
205 return var[int(attribute)]
206 except Exception:
207 return getattr(var, attribute, None)
208
209 def getDictionary(self, var):
210 """
211 Public method to get the attributes of a variable as a dictionary.
212
213 @param var variable to be converted
214 @type any
215 @return dictionary containing the variable attributes
216 @rtype dict
217 """
218 d = {}
219 count = 0
220 for value in var:
221 d[str(count)] = value
222 count += 1
223 if count > MaxItemsToHandle:
224 d[TooLargeAttribute] = TooLargeMessage
225 break
226
227 d["___len___"] = len(var)
228
229 # in case it has additional fields
230 additionals = defaultResolver.getDictionary(var)
231 d.update(additionals)
232
233 return d
234
235
236 ############################################################
237 ## Resolver for Sets and Frozensets
238 ############################################################
239
240
241 class SetResolver(BaseResolver):
242 """
243 Class used to resolve from a set or frozenset.
244 """
245 def resolve(self, var, attribute):
246 """
247 Public method to get an attribute from a variable.
248
249 @param var variable to extract an attribute or value from
250 @type tuple or list
251 @param attribute id of the value to extract
252 @type str
253 @return value of the attribute
254 @rtype any
255 """
256 if attribute in ('___len___', TooLargeAttribute):
257 return None
258
259 if attribute.startswith("ID: "):
260 attribute = attribute.split(None, 1)[1]
261 try:
262 attribute = int(attribute)
263 except Exception:
264 return getattr(var, attribute, None)
265
266 for v in var:
267 if id(v) == attribute:
268 return v
269
270 return None
271
272 def getDictionary(self, var):
273 """
274 Public method to get the attributes of a variable as a dictionary.
275
276 @param var variable to be converted
277 @type any
278 @return dictionary containing the variable attributes
279 @rtype dict
280 """
281 d = {}
282 count = 0
283 for value in var:
284 count += 1
285 d["ID: " + str(id(value))] = value
286 if count > MaxItemsToHandle:
287 d[TooLargeAttribute] = TooLargeMessage
288 break
289
290 d["___len___"] = len(var)
291
292 # in case it has additional fields
293 additionals = defaultResolver.getDictionary(var)
294 d.update(additionals)
295
296 return d
297
298
299 ############################################################
300 ## Resolver for Numpy Arrays
301 ############################################################
302
303
304 class NdArrayResolver(BaseResolver):
305 """
306 Class used to resolve from numpy ndarray including some meta data.
307 """
308 def __isNumeric(self, arr):
309 """
310 Private method to check, if an array is of a numeric type.
311
312 @param arr array to check
313 @type ndarray
314 @return flag indicating a numeric array
315 @rtype bool
316 """
317 try:
318 return arr.dtype.kind in 'biufc'
319 except AttributeError:
320 return False
321
322 def resolve(self, var, attribute):
323 """
324 Public method to get an attribute from a variable.
325
326 @param var variable to extract an attribute or value from
327 @type tuple or list
328 @param attribute id of the value to extract
329 @type str
330 @return value of the attribute
331 @rtype any
332 """
333 if attribute == '__internals__':
334 return defaultResolver.getDictionary(var)
335
336 if attribute == 'min':
337 if self.__isNumeric(var):
338 return var.min()
339 else:
340 return None
341
342 if attribute == 'max':
343 if self.__isNumeric(var):
344 return var.max()
345 else:
346 return None
347
348 if attribute == 'mean':
349 if self.__isNumeric(var):
350 return var.mean()
351 else:
352 return None
353
354 if attribute == 'shape':
355 return var.shape
356
357 if attribute == 'dtype':
358 return var.dtype
359
360 if attribute == 'size':
361 return var.size
362
363 if attribute.startswith('['):
364 container = NdArrayItemsContainer()
365 count = 0
366 for element in var:
367 setattr(container, str(count), element)
368 count += 1
369 if count > MaxItemsToHandle:
370 setattr(container, TooLargeAttribute, TooLargeMessage)
371 break
372 return container
373
374 return None
375
376 def getDictionary(self, var):
377 """
378 Public method to get the attributes of a variable as a dictionary.
379
380 @param var variable to be converted
381 @type any
382 @return dictionary containing the variable attributes
383 @rtype dict
384 """
385 d = {}
386 d['__internals__'] = defaultResolver.getDictionary(var)
387 if var.size > 1024 * 1024:
388 d['min'] = 'ndarray too big, calculating min would slow down' \
389 ' debugging'
390 d['max'] = 'ndarray too big, calculating max would slow down' \
391 ' debugging'
392 else:
393 if self.__isNumeric(var):
394 d['min'] = var.min()
395 d['max'] = var.max()
396 d['mean'] = var.mean()
397 else:
398 d['min'] = 'not a numeric object'
399 d['max'] = 'not a numeric object'
400 d['mean'] = 'not a numeric object'
401 d['shape'] = var.shape
402 d['dtype'] = var.dtype
403 d['size'] = var.size
404 d['[0:{0}]'.format(len(var) - 1)] = list(var[0:MaxItemsToHandle])
405 return d
406
407
408 class NdArrayItemsContainer:
409 """
410 Class to store ndarray items.
411 """
412 pass
413
414
415 ############################################################
416 ## Resolver for Django Multi Value Dictionaries
417 ############################################################
418
419
420 class MultiValueDictResolver(DictResolver):
421 """
422 Class used to resolve from Django multi value dictionaries.
423 """
424 def resolve(self, var, attribute):
425 """
426 Public method to get an attribute from a variable.
427
428 @param var variable to extract an attribute or value from
429 @type dict
430 @param attribute name of the attribute to extract
431 @type str
432 @return value of the attribute
433 @rtype any
434 """
435 if attribute in ('___len___', TooLargeAttribute):
436 return None
437
438 if "(ID:" not in attribute:
439 try:
440 return var[attribute]
441 except Exception:
442 return getattr(var, attribute, None)
443
444 expectedID = int(attribute.split("(ID:")[-1][:-1])
445 for key in var.keys():
446 if id(key) == expectedID:
447 value = var.getlist(key)
448 return value
449
450 return None
451
452 def getDictionary(self, var):
453 """
454 Public method to get the attributes of a variable as a dictionary.
455
456 @param var variable to be converted
457 @type any
458 @return dictionary containing the variable attributes
459 @rtype dict
460 """
461 d = {}
462 count = 0
463 for key in var.keys():
464 count += 1
465 value = var.getlist(key)
466 key = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
467 d[key] = value
468 if count > MaxItemsToHandle:
469 d[TooLargeAttribute] = TooLargeMessage
470 break
471
472 d["___len___"] = len(var)
473
474 return d
475
476
477 ############################################################
478 ## Resolver for array.array
479 ############################################################
480
481
482 class ArrayResolver(BaseResolver):
483 """
484 Class used to resolve from array.array including some meta data.
485 """
486 TypeCodeMap = {
487 "b": "int (signed char)",
488 "B": "int (unsigned char)",
489 "u": "Unicode character (Py_UNICODE)",
490 "h": "int (signed short)",
491 "H": "int (unsigned short)",
492 "i": "int (signed int)",
493 "I": "long (unsigned int)",
494 "l": "int (signed long)",
495 "L": "long (unsigned long)",
496 "q": "int (signed long long)",
497 "Q": "int (unsigned long long)",
498 "f": "float (float)",
499 "d": "float (double)",
500 }
501 def resolve(self, var, attribute):
502 """
503 Public method to get an attribute from a variable.
504
505 @param var variable to extract an attribute or value from
506 @type tuple or list
507 @param attribute id of the value to extract
508 @type str
509 @return value of the attribute
510 @rtype any
511 """
512 if attribute == 'itemsize':
513 return var.itemsize
514
515 if attribute == 'typecode':
516 return var.typecode
517
518 if attribute == 'type':
519 if var.typecode in ArrayResolver.TypeCodeMap:
520 return ArrayResolver.TypeCodeMap[var.typecode]
521 else:
522 return 'illegal type'
523
524 if attribute.startswith('['):
525 container = ArrayItemsContainer()
526 count = 0
527 for element in var:
528 setattr(container, str(count), element)
529 count += 1
530 if count > MaxItemsToHandle:
531 setattr(container, TooLargeAttribute, TooLargeMessage)
532 break
533 return container
534
535 return None
536
537 def getDictionary(self, var):
538 """
539 Public method to get the attributes of a variable as a dictionary.
540
541 @param var variable to be converted
542 @type any
543 @return dictionary containing the variable attributes
544 @rtype dict
545 """
546 d = {}
547 d['typecode'] = var.typecode
548 if var.typecode in ArrayResolver.TypeCodeMap:
549 d['type'] = ArrayResolver.TypeCodeMap[var.typecode]
550 else:
551 d['type'] = 'illegal type'
552 d['itemsize'] = var.itemsize
553 d['[0:{0}]'.format(len(var) - 1)] = var.tolist()[0:MaxItemsToHandle]
554 return d
555
556
557 class ArrayItemsContainer:
558 """
559 Class to store array.array items.
560 """
561 pass
562
563
564 defaultResolver = DefaultResolver()
565 dictResolver = DictResolver()
566 listResolver = ListResolver()
567 setResolver = SetResolver()
568 ndarrayResolver = NdArrayResolver()
569 multiValueDictResolver = MultiValueDictResolver()
570 arrayResolver = ArrayResolver()
571
572 ############################################################
573 ## Methods to determine the type of a variable and the
574 ## resolver class to use
575 ############################################################
576
577 _TypeMap = None
578
579
580 def _initTypeMap():
581 """
582 Protected function to initialize the type map.
583 """
584 global _TypeMap
585
586 _TypeMap = [
587 (type(None), None,),
588 (int, None),
589 (float, None),
590 (complex, None),
591 (str, None),
592 (tuple, listResolver),
593 (list, listResolver),
594 (dict, dictResolver),
595 ]
596
597 try:
598 _TypeMap.append((long, None)) # __IGNORE_WARNING__
599 except Exception:
600 pass # not available on all python versions
601
602 try:
603 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__
604 except Exception:
605 pass # not available on all python versions
606
607 try:
608 _TypeMap.append((set, setResolver)) # __IGNORE_WARNING__
609 except Exception:
610 pass # not available on all python versions
611
612 try:
613 _TypeMap.append((frozenset, setResolver)) # __IGNORE_WARNING__
614 except Exception:
615 pass # not available on all python versions
616
617 try:
618 import array
619 _TypeMap.append((array.array, arrayResolver))
620 except ImportError:
621 pass # array.array may not be available
622
623 try:
624 import numpy
625 _TypeMap.append((numpy.ndarray, ndarrayResolver))
626 except ImportError:
627 pass # numpy may not be installed
628
629 try:
630 from django.utils.datastructures import MultiValueDict
631 _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver))
632 # it should go before dict
633 except ImportError:
634 pass # django may not be installed
635
636
637 def getType(obj):
638 """
639 Public method to get the type information for an object.
640
641 @param obj object to get type information for
642 @type any
643 @return tuple containing the type, type name, type string and resolver
644 @rtype tuple of type, str, str, BaseResolver
645 """
646 typeObject = type(obj)
647 typeName = typeObject.__name__
648 typeStr = str(typeObject)[8:-2]
649
650 if typeStr.startswith(("PyQt5.", "PyQt4.")):
651 resolver = None
652 else:
653 if _TypeMap is None:
654 _initTypeMap()
655
656 for typeData in _TypeMap:
657 if isinstance(obj, typeData[0]):
658 resolver = typeData[1]
659 break
660 else:
661 resolver = defaultResolver
662
663 return typeObject, typeName, typeStr, resolver
664
665 #
666 # eflag: FileType = Python2
667 # eflag: noqa = M601,M702

eric ide

mercurial