Wed, 14 Sep 2016 20:08:16 +0200
Started to improve the variable dumping of the debugger backend.
# -*- coding: utf-8 -*- # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing classes and functions to dump variable contents. """ # # This code was inspired by pydevd. # MaxItemsToHandle = 300 TooLargeMessage = ("Too large to show contents. Max items to show: " + str(MaxItemsToHandle)) TooLargeAttribute = "Too large to be handled." ############################################################ ## Classes implementing resolvers for various compund types ############################################################ class BaseResolver(object): """ Base class of the resolver class tree. """ def resolve(self, var, attribute): """ Public method to get an attribute from a variable. @param var variable to extract an attribute or value from @type any @param attribute name of the attribute to extract @type str @return value of the attribute @rtype any """ raise NotImplementedError def getDictionary(self, var): """ Public method to get the attributes of a variable as a dictionary. @param var variable to be converted @type any @return dictionary containing the variable attributes @rtype dict """ raise NotImplementedError class DefaultResolver(BaseResolver): """ Class used to resolve the default way. """ def resolve(self, var, attribute): """ Public method to get an attribute from a variable. @param var variable to extract an attribute or value from @type any @param attribute name of the attribute to extract @type str @return value of the attribute @rtype any """ return getattr(var, attribute) def getDictionary(self, var): """ Public method to get the attributes of a variable as a dictionary. @param var variable to be converted @type any @return dictionary containing the variable attributes @rtype dict """ names = dir(var) if not names and hasattr(var, "__members__"): names = var.__members__ d = {} for name in names: try: attribute = getattr(var, name) d[name] = attribute except Exception: pass # if we can't get it, simply ignore it return d class DictResolver(BaseResolver): """ Class used to resolve from a dictionary. """ def resolve(self, var, attribute): """ Public method to get an attribute from a variable. @param var variable to extract an attribute or value from @type dict @param attribute name of the attribute to extract @type str @return value of the attribute @rtype any """ if attribute in ('__len__', TooLargeAttribute): return None if "(" not in attribute: try: ## if attribute[0] == "'" and attribute[-1] == "'": ## attribute = attribute[1:-1] return var[attribute] except Exception: return getattr(var, attribute) expectedID = int(attribute.split("(")[-1][:-1]) for key, value in var.items(): if id(key) == expectedID: return value return None def __keyToStr(self, key): if isinstance(key, str): return repr(key) else: return key def getDictionary(self, var): """ Public method to get the attributes of a variable as a dictionary. @param var variable to be converted @type any @return dictionary containing the variable attributes @rtype dict """ d = {} count = 0 for key, value in var.items(): count += 1 key = "{0} ({1})".format(self.__keyToStr(key), id(key)) d[key] = value if count > MaxItemsToHandle: d[TooLargeAttribute] = TooLargeMessage break d["__len__"] = len(var) # in case it has additional fields additionals = defaultResolver.getDictionary(var) d.update(additionals) return d class ListResolver(BaseResolver): """ Class used to resolve from a tuple or list. """ def resolve(self, var, attribute): """ Public method to get an attribute from a variable. @param var variable to extract an attribute or value from @type tuple or list @param attribute name of the attribute to extract @type str @return value of the attribute @rtype any """ if attribute in ('__len__', TooLargeAttribute): return None try: return var[int(attribute)] except Exception: return getattr(var, attribute) def getDictionary(self, var): """ Public method to get the attributes of a variable as a dictionary. @param var variable to be converted @type any @return dictionary containing the variable attributes @rtype dict """ length = len(var) d = {} formatStr = "{0:0" + str(len(str(length))) + "d}" count = 0 for value in var: d[formatStr.format(count)] = value count += 1 if count > MaxItemsToHandle: d[TooLargeAttribute] = TooLargeMessage break d["__len__"] = length # in case it has additional fields additionals = defaultResolver.getDictionary(var) d.update(additionals) return d defaultResolver = DefaultResolver() dictResolver = DictResolver() listResolver = ListResolver() # TODO: add resolver for set and frozenset # TODO: add resolver for numpy arrays # TODO: add resolver for Django MultiValueDict # TODO: add resolver for collections.deque ############################################################ ## Methods to determine the type of a variable and the ## resolver class to use ############################################################ _TypeMap = None def _initTypeMap(): """ Protected function to initialize the type map """ global _TypeMap _TypeMap = [ (type(None), None,), (int, None), (float, None), (complex, None), (str, None), (tuple, listResolver), (list, listResolver), (dict, dictResolver), ] try: _TypeMap.append((long, None)) # __IGNORE_WARNING__ except Exception: pass # not available on all python versions try: _TypeMap.append((unicode, None)) # __IGNORE_WARNING__ except Exception: pass # not available on all python versions def getType(obj): """ Public method to get the type information for an object. @param obj object to get type information for @type any @return tuple containing the type, type name, type string and resolver @rtype tuple of type, str, str, BaseResolver """ typeObject = type(obj) typeName = typeObject.__name__ typeStr = str(typeObject)[8:-2] if typeStr.startswith(("PyQt5.", "PyQt4.")): resolver = None else: if _TypeMap is None: _initTypeMap() for typeData in _TypeMap: if isinstance(obj, typeData[0]): resolver = typeData[1] break else: resolver = defaultResolver return typeObject, typeName, typeStr, resolver