eric6/DebugClients/Python/DebugVariables.py

branch
maintenance
changeset 6989
8b8cadf8d7e9
parent 6952
31602c3f09fd
child 6969
fd7af2312383
equal deleted inserted replaced
6938:7926553b7509 6989:8b8cadf8d7e9
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 if var.size == 0:
395 d['min'] = 'empty array'
396 d['max'] = 'empty array'
397 d['mean'] = 'empty array'
398 else:
399 d['min'] = var.min()
400 d['max'] = var.max()
401 d['mean'] = var.mean()
402 else:
403 d['min'] = 'not a numeric object'
404 d['max'] = 'not a numeric object'
405 d['mean'] = 'not a numeric object'
406 d['shape'] = var.shape
407 d['dtype'] = var.dtype
408 d['size'] = var.size
409 d['[0:{0}]'.format(len(var) - 1)] = list(var[0:MaxItemsToHandle])
410 return d
411
412
413 class NdArrayItemsContainer:
414 """
415 Class to store ndarray items.
416 """
417 pass
418
419
420 ############################################################
421 ## Resolver for Django Multi Value Dictionaries
422 ############################################################
423
424
425 class MultiValueDictResolver(DictResolver):
426 """
427 Class used to resolve from Django multi value dictionaries.
428 """
429 def resolve(self, var, attribute):
430 """
431 Public method to get an attribute from a variable.
432
433 @param var variable to extract an attribute or value from
434 @type dict
435 @param attribute name of the attribute to extract
436 @type str
437 @return value of the attribute
438 @rtype any
439 """
440 if attribute in ('___len___', TooLargeAttribute):
441 return None
442
443 if "(ID:" not in attribute:
444 try:
445 return var[attribute]
446 except Exception:
447 return getattr(var, attribute, None)
448
449 expectedID = int(attribute.split("(ID:")[-1][:-1])
450 for key in var.keys():
451 if id(key) == expectedID:
452 value = var.getlist(key)
453 return value
454
455 return None
456
457 def getDictionary(self, var):
458 """
459 Public method to get the attributes of a variable as a dictionary.
460
461 @param var variable to be converted
462 @type any
463 @return dictionary containing the variable attributes
464 @rtype dict
465 """
466 d = {}
467 count = 0
468 for key in var.keys():
469 count += 1
470 value = var.getlist(key)
471 key = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
472 d[key] = value
473 if count > MaxItemsToHandle:
474 d[TooLargeAttribute] = TooLargeMessage
475 break
476
477 d["___len___"] = len(var)
478
479 return d
480
481
482 ############################################################
483 ## Resolver for array.array
484 ############################################################
485
486
487 class ArrayResolver(BaseResolver):
488 """
489 Class used to resolve from array.array including some meta data.
490 """
491 TypeCodeMap = {
492 "b": "int (signed char)",
493 "B": "int (unsigned char)",
494 "u": "Unicode character (Py_UNICODE)",
495 "h": "int (signed short)",
496 "H": "int (unsigned short)",
497 "i": "int (signed int)",
498 "I": "int (unsigned int)",
499 "l": "int (signed long)",
500 "L": "int (unsigned long)",
501 "q": "int (signed long long)",
502 "Q": "int (unsigned long long)",
503 "f": "float (float)",
504 "d": "float (double)",
505 }
506
507 def resolve(self, var, attribute):
508 """
509 Public method to get an attribute from a variable.
510
511 @param var variable to extract an attribute or value from
512 @type tuple or list
513 @param attribute id of the value to extract
514 @type str
515 @return value of the attribute
516 @rtype any
517 """
518 if attribute == 'itemsize':
519 return var.itemsize
520
521 if attribute == 'typecode':
522 return var.typecode
523
524 if attribute == 'type':
525 if var.typecode in ArrayResolver.TypeCodeMap:
526 return ArrayResolver.TypeCodeMap[var.typecode]
527 else:
528 return 'illegal type'
529
530 if attribute.startswith('['):
531 container = ArrayItemsContainer()
532 count = 0
533 for element in var:
534 setattr(container, str(count), element)
535 count += 1
536 if count > MaxItemsToHandle:
537 setattr(container, TooLargeAttribute, TooLargeMessage)
538 break
539 return container
540
541 return None
542
543 def getDictionary(self, var):
544 """
545 Public method to get the attributes of a variable as a dictionary.
546
547 @param var variable to be converted
548 @type any
549 @return dictionary containing the variable attributes
550 @rtype dict
551 """
552 d = {}
553 d['typecode'] = var.typecode
554 if var.typecode in ArrayResolver.TypeCodeMap:
555 d['type'] = ArrayResolver.TypeCodeMap[var.typecode]
556 else:
557 d['type'] = 'illegal type'
558 d['itemsize'] = var.itemsize
559 d['[0:{0}]'.format(len(var) - 1)] = var.tolist()[0:MaxItemsToHandle]
560 return d
561
562
563 class ArrayItemsContainer:
564 """
565 Class to store array.array items.
566 """
567 pass
568
569
570 defaultResolver = DefaultResolver()
571 dictResolver = DictResolver()
572 listResolver = ListResolver()
573 setResolver = SetResolver()
574 ndarrayResolver = NdArrayResolver()
575 multiValueDictResolver = MultiValueDictResolver()
576 arrayResolver = ArrayResolver()
577
578 ############################################################
579 ## Methods to determine the type of a variable and the
580 ## resolver class to use
581 ############################################################
582
583 _TypeMap = None
584
585
586 def _initTypeMap():
587 """
588 Protected function to initialize the type map.
589 """
590 global _TypeMap
591
592 _TypeMap = [
593 (type(None), None,),
594 (int, None),
595 (float, None),
596 (complex, None),
597 (str, None),
598 (tuple, listResolver),
599 (list, listResolver),
600 (dict, dictResolver),
601 ]
602
603 try:
604 _TypeMap.append((long, None)) # __IGNORE_WARNING__
605 except Exception:
606 pass # not available on all python versions
607
608 try:
609 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__
610 except Exception:
611 pass # not available on all python versions
612
613 try:
614 _TypeMap.append((set, setResolver)) # __IGNORE_WARNING__
615 except Exception:
616 pass # not available on all python versions
617
618 try:
619 _TypeMap.append((frozenset, setResolver)) # __IGNORE_WARNING__
620 except Exception:
621 pass # not available on all python versions
622
623 try:
624 import array
625 _TypeMap.append((array.array, arrayResolver))
626 except ImportError:
627 pass # array.array may not be available
628
629 try:
630 import numpy
631 _TypeMap.append((numpy.ndarray, ndarrayResolver))
632 except ImportError:
633 pass # numpy may not be installed
634
635 try:
636 from django.utils.datastructures import MultiValueDict
637 _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver))
638 # it should go before dict
639 except ImportError:
640 pass # django may not be installed
641
642
643 def getType(obj):
644 """
645 Public method to get the type information for an object.
646
647 @param obj object to get type information for
648 @type any
649 @return tuple containing the type, type name, type string and resolver
650 @rtype tuple of type, str, str, BaseResolver
651 """
652 typeObject = type(obj)
653 typeName = typeObject.__name__
654 typeStr = str(typeObject)[8:-2]
655
656 if typeStr.startswith(("PyQt5.", "PyQt4.")):
657 resolver = None
658 else:
659 if _TypeMap is None:
660 _initTypeMap()
661
662 for typeData in _TypeMap:
663 if isinstance(obj, typeData[0]):
664 resolver = typeData[1]
665 break
666 else:
667 resolver = defaultResolver
668
669 return typeObject, typeName, typeStr, resolver
670
671 #
672 # eflag: noqa = M702

eric ide

mercurial