eric6/DebugClients/Python/DebugVariables.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6952
31602c3f09fd
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2019 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 = "{0} (ID:{1})".format(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": "int (unsigned int)",
494 "l": "int (signed long)",
495 "L": "int (unsigned long)",
496 "q": "int (signed long long)",
497 "Q": "int (unsigned long long)",
498 "f": "float (float)",
499 "d": "float (double)",
500 }
501
502 def resolve(self, var, attribute):
503 """
504 Public method to get an attribute from a variable.
505
506 @param var variable to extract an attribute or value from
507 @type tuple or list
508 @param attribute id of the value to extract
509 @type str
510 @return value of the attribute
511 @rtype any
512 """
513 if attribute == 'itemsize':
514 return var.itemsize
515
516 if attribute == 'typecode':
517 return var.typecode
518
519 if attribute == 'type':
520 if var.typecode in ArrayResolver.TypeCodeMap:
521 return ArrayResolver.TypeCodeMap[var.typecode]
522 else:
523 return 'illegal type'
524
525 if attribute.startswith('['):
526 container = ArrayItemsContainer()
527 count = 0
528 for element in var:
529 setattr(container, str(count), element)
530 count += 1
531 if count > MaxItemsToHandle:
532 setattr(container, TooLargeAttribute, TooLargeMessage)
533 break
534 return container
535
536 return None
537
538 def getDictionary(self, var):
539 """
540 Public method to get the attributes of a variable as a dictionary.
541
542 @param var variable to be converted
543 @type any
544 @return dictionary containing the variable attributes
545 @rtype dict
546 """
547 d = {}
548 d['typecode'] = var.typecode
549 if var.typecode in ArrayResolver.TypeCodeMap:
550 d['type'] = ArrayResolver.TypeCodeMap[var.typecode]
551 else:
552 d['type'] = 'illegal type'
553 d['itemsize'] = var.itemsize
554 d['[0:{0}]'.format(len(var) - 1)] = var.tolist()[0:MaxItemsToHandle]
555 return d
556
557
558 class ArrayItemsContainer:
559 """
560 Class to store array.array items.
561 """
562 pass
563
564
565 defaultResolver = DefaultResolver()
566 dictResolver = DictResolver()
567 listResolver = ListResolver()
568 setResolver = SetResolver()
569 ndarrayResolver = NdArrayResolver()
570 multiValueDictResolver = MultiValueDictResolver()
571 arrayResolver = ArrayResolver()
572
573 ############################################################
574 ## Methods to determine the type of a variable and the
575 ## resolver class to use
576 ############################################################
577
578 _TypeMap = None
579
580
581 def _initTypeMap():
582 """
583 Protected function to initialize the type map.
584 """
585 global _TypeMap
586
587 _TypeMap = [
588 (type(None), None,),
589 (int, None),
590 (float, None),
591 (complex, None),
592 (str, None),
593 (tuple, listResolver),
594 (list, listResolver),
595 (dict, dictResolver),
596 ]
597
598 try:
599 _TypeMap.append((long, None)) # __IGNORE_WARNING__
600 except Exception:
601 pass # not available on all python versions
602
603 try:
604 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__
605 except Exception:
606 pass # not available on all python versions
607
608 try:
609 _TypeMap.append((set, setResolver)) # __IGNORE_WARNING__
610 except Exception:
611 pass # not available on all python versions
612
613 try:
614 _TypeMap.append((frozenset, setResolver)) # __IGNORE_WARNING__
615 except Exception:
616 pass # not available on all python versions
617
618 try:
619 import array
620 _TypeMap.append((array.array, arrayResolver))
621 except ImportError:
622 pass # array.array may not be available
623
624 try:
625 import numpy
626 _TypeMap.append((numpy.ndarray, ndarrayResolver))
627 except ImportError:
628 pass # numpy may not be installed
629
630 try:
631 from django.utils.datastructures import MultiValueDict
632 _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver))
633 # it should go before dict
634 except ImportError:
635 pass # django may not be installed
636
637
638 def getType(obj):
639 """
640 Public method to get the type information for an object.
641
642 @param obj object to get type information for
643 @type any
644 @return tuple containing the type, type name, type string and resolver
645 @rtype tuple of type, str, str, BaseResolver
646 """
647 typeObject = type(obj)
648 typeName = typeObject.__name__
649 typeStr = str(typeObject)[8:-2]
650
651 if typeStr.startswith(("PyQt5.", "PyQt4.")):
652 resolver = None
653 else:
654 if _TypeMap is None:
655 _initTypeMap()
656
657 for typeData in _TypeMap:
658 if isinstance(obj, typeData[0]):
659 resolver = typeData[1]
660 break
661 else:
662 resolver = defaultResolver
663
664 return typeObject, typeName, typeStr, resolver
665
666 #
667 # eflag: noqa = M702

eric ide

mercurial