eric6/DebugClients/Python/DebugVariables.py

branch
Variables Viewer
changeset 6969
fd7af2312383
parent 6952
31602c3f09fd
child 6978
720247f98e1f
equal deleted inserted replaced
6968:c634f51e40ec 6969:fd7af2312383
5 5
6 """ 6 """
7 Module implementing classes and functions to dump variable contents. 7 Module implementing classes and functions to dump variable contents.
8 """ 8 """
9 9
10 import sys
11
12 from DebugConfig import ConfigQtNames, BatchSize
13
10 # 14 #
11 # This code was inspired by pydevd. 15 # This code was inspired by pydevd.
12 # 16 #
13 17
14 MaxItemsToHandle = 300 18 if sys.version_info[0] > 2:
15 TooLargeMessage = ("Too large to show contents. Max items to show: " + 19 basestring = str
16 str(MaxItemsToHandle))
17 TooLargeAttribute = "Too large to be handled."
18 20
19 ############################################################ 21 ############################################################
20 ## Classes implementing resolvers for various compund types 22 ## Classes implementing resolvers for various compund types
21 ############################################################ 23 ############################################################
22 24
23 25
24 class BaseResolver(object): 26 class BaseResolver(object):
25 """ 27 """
26 Base class of the resolver class tree. 28 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 """ 29 """
66 def resolve(self, var, attribute): 30 def resolve(self, var, attribute):
67 """ 31 """
68 Public method to get an attribute from a variable. 32 Public method to get an attribute from a variable.
69 33
99 63
100 return d 64 return d
101 65
102 66
103 ############################################################ 67 ############################################################
68 ## Default Resolver
69 ############################################################
70
71
72 class DefaultResolver(BaseResolver):
73 """
74 Class used to resolve the default way.
75 """
76 def getDictionary(self, var):
77 """
78 Public method to get the attributes of a variable as a dictionary.
79
80 @param var variable to be converted
81 @type any
82 @return dictionary containing the variable attributes
83 @rtype dict
84 """
85 names = dir(var)
86 if not names and hasattr(var, "__members__"):
87 names = var.__members__
88
89 d = {}
90 for name in names:
91 try:
92 attribute = getattr(var, name)
93 d[name] = attribute
94 except Exception:
95 pass # if we can't get it, simply ignore it
96
97 yield -1, d
98 while True:
99 yield -2, {}
100
101
102 ############################################################
104 ## Resolver for Dictionaries 103 ## Resolver for Dictionaries
105 ############################################################ 104 ############################################################
106 105
107 106
108 class DictResolver(BaseResolver): 107 class DictResolver(BaseResolver):
118 @param attribute name of the attribute to extract 117 @param attribute name of the attribute to extract
119 @type str 118 @type str
120 @return value of the attribute 119 @return value of the attribute
121 @rtype any 120 @rtype any
122 """ 121 """
123 if attribute in ('___len___', TooLargeAttribute): 122 if " (ID:" not in attribute:
124 return None
125
126 if "(ID:" not in attribute:
127 try: 123 try:
128 return var[attribute] 124 return var[attribute]
129 except Exception: 125 except Exception:
130 return getattr(var, attribute, None) 126 return getattr(var, attribute, None)
131 127
132 expectedID = int(attribute.split("(ID:")[-1][:-1]) 128 expectedID = int(attribute.split(" (ID:")[-1][:-1])
133 for key, value in var.items(): 129 for key, value in var.items():
134 if id(key) == expectedID: 130 if id(key) == expectedID:
135 return value 131 return value
136 132
137 return None 133 return None
143 @param key key to be converted 139 @param key key to be converted
144 @type any 140 @type any
145 @return string representation of the given key 141 @return string representation of the given key
146 @rtype str 142 @rtype str
147 """ 143 """
148 if isinstance(key, str): 144 if isinstance(key, basestring):
149 return repr(key) 145 key = repr(key)
150 else: 146 # Special handling for Python2 unicode strings and bytes object
151 return key 147 # Raw and f-Strings are always converted to (unicode) str
152 148 if key[0] in 'ub':
153 def getDictionary(self, var): 149 key = key[1:]
154 """ 150
155 Public method to get the attributes of a variable as a dictionary. 151 return key # __IGNORE_WARNING_M834__
156 152
157 @param var variable to be converted 153 def getDictionary(self, var):
158 @type any 154 """
159 @return dictionary containing the variable attributes 155 Public method to get the attributes of a variable as a dictionary.
160 @rtype dict 156
161 """ 157 @param var variable to be converted
162 d = {} 158 @type any
163 count = 0 159 @return dictionary containing the variable attributes
164 for key, value in var.items(): 160 @rtype dict
165 count += 1 161 """
162 d = {}
163 start = count = 0
164 allItems = list(var.items())
165 try:
166 # Fast path: all items from same type
167 allItems.sort(key=lambda x: x[0])
168 except TypeError:
169 # Slow path: only sort items with same type (Py3 only)
170 allItems.sort(key=lambda x: (str(x[0]), x[0]))
171
172 for key, value in allItems:
166 key = "{0} (ID:{1})".format(self.keyToStr(key), id(key)) 173 key = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
167 d[key] = value 174 d[key] = value
168 if count > MaxItemsToHandle: 175 count += 1
169 d[TooLargeAttribute] = TooLargeMessage 176 if count >= BatchSize:
170 break 177 yield start, d
171 178 start += count
172 d["___len___"] = len(var) 179 count = 0
180 d = {}
181
182 if d:
183 yield start, d
173 184
174 # in case it has additional fields 185 # in case it has additional fields
175 additionals = defaultResolver.getDictionary(var) 186 d = super(DictResolver, self).getDictionary(var)
176 d.update(additionals) 187 yield -1, d
177 188
178 return d 189 while True:
190 yield -2, {}
179 191
180 192
181 ############################################################ 193 ############################################################
182 ## Resolver for Lists and Tuples 194 ## Resolver for Lists and Tuples
183 ############################################################ 195 ############################################################
196 @param attribute name of the attribute to extract 208 @param attribute name of the attribute to extract
197 @type str 209 @type str
198 @return value of the attribute 210 @return value of the attribute
199 @rtype any 211 @rtype any
200 """ 212 """
201 if attribute in ('___len___', TooLargeAttribute):
202 return None
203
204 try: 213 try:
205 return var[int(attribute)] 214 return var[int(attribute)]
206 except Exception: 215 except Exception:
207 return getattr(var, attribute, None) 216 return getattr(var, attribute, None)
208 217
214 @type any 223 @type any
215 @return dictionary containing the variable attributes 224 @return dictionary containing the variable attributes
216 @rtype dict 225 @rtype dict
217 """ 226 """
218 d = {} 227 d = {}
219 count = 0 228 start = count = 0
220 for value in var: 229 for idx, value in enumerate(var):
221 d[str(count)] = value 230 d[str(idx)] = value
222 count += 1 231 count += 1
223 if count > MaxItemsToHandle: 232 if count >= BatchSize:
224 d[TooLargeAttribute] = TooLargeMessage 233 yield start, d
225 break 234 start = idx + 1
226 235 count = 0
227 d["___len___"] = len(var) 236 d = {}
237
238 if d:
239 yield start, d
228 240
229 # in case it has additional fields 241 # in case it has additional fields
230 additionals = defaultResolver.getDictionary(var) 242 d = super(ListResolver, self).getDictionary(var)
231 d.update(additionals) 243 yield -1, d
232 244
233 return d 245 while True:
234 246 yield -2, {}
247
235 248
236 ############################################################ 249 ############################################################
237 ## Resolver for Sets and Frozensets 250 ## Resolver for Sets and Frozensets
238 ############################################################ 251 ############################################################
239 252
251 @param attribute id of the value to extract 264 @param attribute id of the value to extract
252 @type str 265 @type str
253 @return value of the attribute 266 @return value of the attribute
254 @rtype any 267 @rtype any
255 """ 268 """
256 if attribute in ('___len___', TooLargeAttribute): 269 if attribute.startswith("'ID: "):
257 return None 270 attribute = attribute.split(None, 1)[1][:-1]
258
259 if attribute.startswith("ID: "):
260 attribute = attribute.split(None, 1)[1]
261 try: 271 try:
262 attribute = int(attribute) 272 attribute = int(attribute)
263 except Exception: 273 except Exception:
264 return getattr(var, attribute, None) 274 return getattr(var, attribute, None)
265 275
277 @type any 287 @type any
278 @return dictionary containing the variable attributes 288 @return dictionary containing the variable attributes
279 @rtype dict 289 @rtype dict
280 """ 290 """
281 d = {} 291 d = {}
282 count = 0 292 start = count = 0
283 for value in var: 293 for value in var:
284 count += 1 294 count += 1
285 d["ID: " + str(id(value))] = value 295 d["'ID: {0}'".format(id(value))] = value
286 if count > MaxItemsToHandle: 296 if count >= BatchSize:
287 d[TooLargeAttribute] = TooLargeMessage 297 yield start, d
288 break 298 start += count
289 299 count = 0
290 d["___len___"] = len(var) 300 d = {}
301
302 if d:
303 yield start, d
291 304
292 # in case it has additional fields 305 # in case it has additional fields
293 additionals = defaultResolver.getDictionary(var) 306 additionals = super(SetResolver, self).getDictionary(var)
294 d.update(additionals) 307 yield -1, additionals
295 308
296 return d 309 while True:
297 310 yield -2, {}
311
298 312
299 ############################################################ 313 ############################################################
300 ## Resolver for Numpy Arrays 314 ## Resolver for Numpy Arrays
301 ############################################################ 315 ############################################################
302 316
328 @param attribute id of the value to extract 342 @param attribute id of the value to extract
329 @type str 343 @type str
330 @return value of the attribute 344 @return value of the attribute
331 @rtype any 345 @rtype any
332 """ 346 """
333 if attribute == '__internals__':
334 return defaultResolver.getDictionary(var)
335
336 if attribute == 'min': 347 if attribute == 'min':
337 if self.__isNumeric(var): 348 if self.__isNumeric(var):
338 return var.min() 349 return var.min()
339 else: 350 else:
340 return None 351 return None
349 if self.__isNumeric(var): 360 if self.__isNumeric(var):
350 return var.mean() 361 return var.mean()
351 else: 362 else:
352 return None 363 return None
353 364
354 if attribute == 'shape': 365 try:
355 return var.shape 366 return var[int(attribute)]
356 367 except Exception:
357 if attribute == 'dtype': 368 return getattr(var, attribute, None)
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 369
374 return None 370 return None
375 371
376 def getDictionary(self, var): 372 def getDictionary(self, var):
377 """ 373 """
381 @type any 377 @type any
382 @return dictionary containing the variable attributes 378 @return dictionary containing the variable attributes
383 @rtype dict 379 @rtype dict
384 """ 380 """
385 d = {} 381 d = {}
386 d['__internals__'] = defaultResolver.getDictionary(var) 382 start = count = 0
383 allItems = var.tolist()
384
385 for idx, value in enumerate(allItems):
386 d[str(idx)] = value
387 count += 1
388 if count >= BatchSize:
389 yield start, d
390 start += count
391 count = 0
392 d = {}
393
394 if d:
395 yield start, d
396
397 # in case it has additional fields
398 d = super(NdArrayResolver, self).getDictionary(var)
399
387 if var.size > 1024 * 1024: 400 if var.size > 1024 * 1024:
388 d['min'] = 'ndarray too big, calculating min would slow down' \ 401 d['min'] = 'ndarray too big, calculating min would slow down' \
389 ' debugging' 402 ' debugging'
390 d['max'] = 'ndarray too big, calculating max would slow down' \ 403 d['max'] = 'ndarray too big, calculating max would slow down' \
391 ' debugging' 404 ' debugging'
405 d['mean'] = 'ndarray too big, calculating mean would slow down' \
406 ' debugging'
407 elif self.__isNumeric(var):
408 if var.size == 0:
409 d['min'] = 'empty array'
410 d['max'] = 'empty array'
411 d['mean'] = 'empty array'
412 else:
413 d['min'] = var.min()
414 d['max'] = var.max()
415 d['mean'] = var.mean()
392 else: 416 else:
393 if self.__isNumeric(var): 417 d['min'] = 'not a numeric object'
394 if var.size == 0: 418 d['max'] = 'not a numeric object'
395 d['min'] = 'empty array' 419 d['mean'] = 'not a numeric object'
396 d['max'] = 'empty array' 420
397 d['mean'] = 'empty array' 421 yield -1, d
398 else: 422
399 d['min'] = var.min() 423 while True:
400 d['max'] = var.max() 424 yield -2, {}
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 425
419 426
420 ############################################################ 427 ############################################################
421 ## Resolver for Django Multi Value Dictionaries 428 ## Resolver for Django Multi Value Dictionaries
422 ############################################################ 429 ############################################################
435 @param attribute name of the attribute to extract 442 @param attribute name of the attribute to extract
436 @type str 443 @type str
437 @return value of the attribute 444 @return value of the attribute
438 @rtype any 445 @rtype any
439 """ 446 """
440 if attribute in ('___len___', TooLargeAttribute): 447 if " (ID:" not in attribute:
441 return None
442
443 if "(ID:" not in attribute:
444 try: 448 try:
445 return var[attribute] 449 return var[attribute]
446 except Exception: 450 except Exception:
447 return getattr(var, attribute, None) 451 return getattr(var, attribute, None)
448 452
449 expectedID = int(attribute.split("(ID:")[-1][:-1]) 453 expectedID = int(attribute.split(" (ID:")[-1][:-1])
450 for key in var.keys(): 454 for key in var.keys():
451 if id(key) == expectedID: 455 if id(key) == expectedID:
452 value = var.getlist(key) 456 return var.getlist(key)
453 return value
454 457
455 return None 458 return None
456 459
457 def getDictionary(self, var): 460 def getDictionary(self, var):
458 """ 461 """
462 @type any 465 @type any
463 @return dictionary containing the variable attributes 466 @return dictionary containing the variable attributes
464 @rtype dict 467 @rtype dict
465 """ 468 """
466 d = {} 469 d = {}
467 count = 0 470 start = count = 0
468 for key in var.keys(): 471 allKeys = list(var.keys())
472 try:
473 # Fast path: all items from same type
474 allKeys.sort()
475 except TypeError:
476 # Slow path: only sort items with same type (Py3 only)
477 allKeys.sort(key=lambda x: (str(x), x))
478
479 for key in allKeys:
480 dkey = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
481 d[dkey] = var.getlist(key)
469 count += 1 482 count += 1
470 value = var.getlist(key) 483 if count >= BatchSize:
471 key = "{0} (ID:{1})".format(self.keyToStr(key), id(key)) 484 yield start, d
472 d[key] = value 485 start += count
473 if count > MaxItemsToHandle: 486 count = 0
474 d[TooLargeAttribute] = TooLargeMessage 487 d = {}
475 break 488
476 489 if d:
477 d["___len___"] = len(var) 490 yield start, d
478 491
479 return d 492 # in case it has additional fields
480 493 d = super(DictResolver, self).getDictionary(var)
494 yield -1, d
495
496 while True:
497 yield -2, {}
498
481 499
482 ############################################################ 500 ############################################################
483 ## Resolver for array.array 501 ## Resolver for array.array
484 ############################################################ 502 ############################################################
485 503
513 @param attribute id of the value to extract 531 @param attribute id of the value to extract
514 @type str 532 @type str
515 @return value of the attribute 533 @return value of the attribute
516 @rtype any 534 @rtype any
517 """ 535 """
518 if attribute == 'itemsize': 536 try:
519 return var.itemsize 537 return var[int(attribute)]
520 538 except Exception:
521 if attribute == 'typecode': 539 return getattr(var, attribute, None)
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 540
541 return None 541 return None
542 542
543 def getDictionary(self, var): 543 def getDictionary(self, var):
544 """ 544 """
548 @type any 548 @type any
549 @return dictionary containing the variable attributes 549 @return dictionary containing the variable attributes
550 @rtype dict 550 @rtype dict
551 """ 551 """
552 d = {} 552 d = {}
553 d['typecode'] = var.typecode 553 start = count = 0
554 if var.typecode in ArrayResolver.TypeCodeMap: 554 allItems = var.tolist()
555 d['type'] = ArrayResolver.TypeCodeMap[var.typecode] 555
556 else: 556 for idx, value in enumerate(allItems):
557 d['type'] = 'illegal type' 557 d[str(idx)] = value
558 d['itemsize'] = var.itemsize 558 count += 1
559 d['[0:{0}]'.format(len(var) - 1)] = var.tolist()[0:MaxItemsToHandle] 559 if count >= BatchSize:
560 return d 560 yield start, d
561 561 start += count
562 562 count = 0
563 class ArrayItemsContainer: 563 d = {}
564 """ 564
565 Class to store array.array items. 565 if d:
566 """ 566 yield start, d
567 pass 567
568 # in case it has additional fields
569 d = super(ArrayResolver, self).getDictionary(var)
570
571 # Special data for array type: convert typecode to readable text
572 d['type'] = self.TypeCodeMap.get(var.typecode, 'illegal type')
573
574 yield -1, d
575
576 while True:
577 yield -2, {}
568 578
569 579
570 defaultResolver = DefaultResolver() 580 defaultResolver = DefaultResolver()
571 dictResolver = DictResolver() 581 dictResolver = DictResolver()
572 listResolver = ListResolver() 582 listResolver = ListResolver()
596 (complex, None), 606 (complex, None),
597 (str, None), 607 (str, None),
598 (tuple, listResolver), 608 (tuple, listResolver),
599 (list, listResolver), 609 (list, listResolver),
600 (dict, dictResolver), 610 (dict, dictResolver),
611 (set, setResolver),
612 (frozenset, setResolver),
601 ] 613 ]
602 614
603 try: 615 try:
604 _TypeMap.append((long, None)) # __IGNORE_WARNING__ 616 _TypeMap.append((long, None)) # __IGNORE_WARNING__
605 except Exception: 617 except Exception:
608 try: 620 try:
609 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__ 621 _TypeMap.append((unicode, None)) # __IGNORE_WARNING__
610 except Exception: 622 except Exception:
611 pass # not available on all python versions 623 pass # not available on all python versions
612 624
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: 625 try:
624 import array 626 import array
625 _TypeMap.append((array.array, arrayResolver)) 627 _TypeMap.append((array.array, arrayResolver))
626 except ImportError: 628 except ImportError:
627 pass # array.array may not be available 629 pass # array.array may not be available
632 except ImportError: 634 except ImportError:
633 pass # numpy may not be installed 635 pass # numpy may not be installed
634 636
635 try: 637 try:
636 from django.utils.datastructures import MultiValueDict 638 from django.utils.datastructures import MultiValueDict
639 # it should go before dict
637 _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver)) 640 _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver))
638 # it should go before dict
639 except ImportError: 641 except ImportError:
640 pass # django may not be installed 642 pass # django may not be installed
641 643
642 644
643 def getType(obj): 645 def getType(obj):
644 """ 646 """
645 Public method to get the type information for an object. 647 Public method to get the type information for an object.
646 648
647 @param obj object to get type information for 649 @param obj object to get type information for
648 @type any 650 @type any
649 @return tuple containing the type, type name, type string and resolver 651 @return tuple containing the type name, type string and resolver
650 @rtype tuple of type, str, str, BaseResolver 652 @rtype tuple of str, str, BaseResolver
651 """ 653 """
652 typeObject = type(obj) 654 typeObject = type(obj)
653 typeName = typeObject.__name__ 655 typeName = typeObject.__name__
654 typeStr = str(typeObject)[8:-2] 656 # Between PyQt and PySide the returned type is different (class vs. type)
655 657 typeStr = str(typeObject).split(' ', 1)[-1]
656 if typeStr.startswith(("PyQt5.", "PyQt4.")): 658 typeStr = typeStr[1:-2]
659
660 if typeStr.startswith(ConfigQtNames):
657 resolver = None 661 resolver = None
658 else: 662 else:
659 if _TypeMap is None: 663 if _TypeMap is None:
660 _initTypeMap() 664 _initTypeMap()
661 665
662 for typeData in _TypeMap: 666 for typeData, resolver in _TypeMap: # __IGNORE_WARNING_M507__
663 if isinstance(obj, typeData[0]): 667 if isinstance(obj, typeData):
664 resolver = typeData[1]
665 break 668 break
666 else: 669 else:
667 resolver = defaultResolver 670 resolver = defaultResolver
668 671
669 return typeObject, typeName, typeStr, resolver 672 return typeName, typeStr, resolver
670 673
671 # 674 #
672 # eflag: noqa = M702 675 # eflag: noqa = M702

eric ide

mercurial