Mon, 19 Sep 2016 22:47:52 +0200
Preparation for combining the Python 2 and 3 debug clients. Base is Python 3.
# -*- 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 @exception NotImplementedError raised to indicate a missing implementation """ # __IGNORE_WARNING_D235__ 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 @exception NotImplementedError raised to indicate a missing implementation """ # __IGNORE_WARNING_D235__ 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 "(ID:" not in attribute: try: return var[attribute] except Exception: return getattr(var, attribute) expectedID = int(attribute.split("(ID:")[-1][:-1]) for key, value in var.items(): if id(key) == expectedID: return value return None def __keyToStr(self, key): """ Private method to get a string representation for a key. @param key key to be converted @type any @return string representation of the given key @rtype str """ 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} (ID:{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 """ d = {} count = 0 for value in var: d[str(count)] = value count += 1 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 SetResolver(BaseResolver): """ Class used to resolve from a set or frozenset. """ 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 id of the value to extract @type str @return value of the attribute @rtype any """ if attribute in ('___len___', TooLargeAttribute): return None if attribute.startswith("ID:"): attribute = attribute.split(None, 1)[1] try: attribute = int(attribute) except Exception: return getattr(var, attribute) for v in var: if id(v) == attribute: return v return None 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 value in var: count += 1 d["ID: " + str(id(value))] = 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 defaultResolver = DefaultResolver() dictResolver = DictResolver() listResolver = ListResolver() setResolver = SetResolver() # 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 try: _TypeMap.append((set, setResolver)) # __IGNORE_WARNING__ except Exception: pass # not available on all python versions try: _TypeMap.append((frozenset, setResolver)) # __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 # # eflag: noqa = M702