diff -r e4a11c02374a -r 74e000797a93 DebugClients/Python3/DebugVariables.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DebugClients/Python3/DebugVariables.py Wed Sep 14 20:08:16 2016 +0200 @@ -0,0 +1,282 @@ +# -*- 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