--- a/eric6/DebugClients/Python/DebugClientBase.py Sun Apr 21 17:27:52 2019 +0200 +++ b/eric6/DebugClients/Python/DebugClientBase.py Sun Apr 21 21:20:24 2019 +0200 @@ -26,7 +26,7 @@ import DebugVariables from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__ from AsyncFile import AsyncFile, AsyncPendingWrite -from DebugConfig import ConfigVarTypeStrings +from DebugConfig import ConfigQtNames, ConfigVarTypeStrings from FlexCompleter import Completer from DebugUtilities import prepareJsonCommand from BreakpointWatch import Breakpoint, Watch @@ -379,14 +379,12 @@ if method == "RequestVariables": self.__dumpVariables( - params["frameNumber"], params["scope"], params["filters"], - params["maxSize"]) + params["frameNumber"], params["scope"], params["filters"]) elif method == "RequestVariable": self.__dumpVariable( params["variable"], params["frameNumber"], - params["scope"], params["filters"], - params["maxSize"]) + params["scope"], params["filters"]) elif method == "RequestThreadList": self.dumpThreadList() @@ -1429,7 +1427,7 @@ # reset coding self.__coding = self.defaultCoding - def __dumpVariables(self, frmnr, scope, filterList, maxSize): + def __dumpVariables(self, frmnr, scope, filterList): """ Private method to return the variables of a frame to the debug server. @@ -1447,6 +1445,7 @@ if self.currentThread is None: return + self.resolverCache = [{}, {}] frmnr += self.currentThread.skipFrames if scope == 0: self.framenr = frmnr @@ -1468,22 +1467,18 @@ scope = -1 else: varDict = f.f_locals - - varlist = [] - if scope != -1: - keylist = varDict.keys() - - vlist = self.__formatVariablesList( - keylist, varDict, scope, filterList, maxSize=maxSize) - varlist.extend(vlist) + if scope == -1: + varlist = [] + else: + varlist = self.__formatVariablesList(varDict, scope, filterList) self.sendJsonCommand("ResponseVariables", { "scope": scope, "variables": varlist, }) - def __dumpVariable(self, var, frmnr, scope, filterList, maxSize): + def __dumpVariable(self, var, frmnr, scope, filterList): """ Private method to return the variables of a frame to the debug server. @@ -1523,12 +1518,16 @@ varlist = [] - if scope != -1: + if scope != -1 and str(var) in self.resolverCache[scope]: + varGen = self.resolverCache[scope][str(var)] + idx, varDict = next(varGen) + var.insert(0, idx) + varlist = self.__formatVariablesList(varDict, scope, filterList) + elif scope != -1: variable = varDict + # Lookup the wanted attribute for attribute in var: - attribute = self.__extractIndicators(attribute)[0] - typeObject, typeName, typeStr, resolver = \ - DebugVariables.getType(variable) + _, _, resolver = DebugVariables.getType(variable) if resolver: variable = resolver.resolve(variable, attribute) if variable is None: @@ -1536,26 +1535,35 @@ else: break - + + idx = -2 + # If found, get the details of attribute if variable is not None: - typeObject, typeName, typeStr, resolver = \ - DebugVariables.getType(variable) - if typeStr.startswith(("PyQt5.", "PyQt4.")): - vlist = self.__formatQtVariable(variable, typeName) - varlist.extend(vlist) + typeName, typeStr, resolver = DebugVariables.getType(variable) + if typeStr.startswith(ConfigQtNames): + # Gently handle exception which could occure as special + # cases, e.g. already deleted C++ objects, str conversion.. + try: + varlist = self.__formatQtVariable(variable, typeName) + except Exception: + varlist = [] + idx = -1 elif resolver: - varDict = resolver.getDictionary(variable) - vlist = self.__formatVariablesList( - list(varDict.keys()), varDict, scope, filterList, - maxSize=maxSize) - varlist.extend(vlist) + varGen = resolver.getDictionary(variable) + self.resolverCache[scope][str(var)] = varGen + + idx, varDict = next(varGen) + varlist = self.__formatVariablesList( + varDict, scope, filterList) + + var.insert(0, idx) self.sendJsonCommand("ResponseVariable", { "scope": scope, "variable": var, "variables": varlist, }) - + def __extractIndicators(self, var): """ Private method to extract the indicator string from a variable text. @@ -1702,7 +1710,7 @@ varlist.append(("data", "str", "{0}".format(value.data()))) elif qttype == 'QDomComment': varlist.append(("data", "str", "{0}".format(value.data()))) - elif qttype == "QDomDocument": + elif qttype == 'QDomDocument': varlist.append(("text", "str", "{0}".format(value.toString()))) elif qttype == 'QDomElement': varlist.append(("tagName", "str", "{0}".format(value.tagName()))) @@ -1715,10 +1723,14 @@ varlist.append( ("address", "QHostAddress", "{0}".format(value.toString()))) + # PySide specific + elif qttype == 'EnumType': # Not in PyQt possible + for key, value in value.values.items(): + varlist.append((key, qttype, "{0}".format(int(value)))) + return varlist - def __formatVariablesList(self, keylist, dict_, scope, filterList=None, - formatSequences=False, maxSize=0): + def __formatVariablesList(self, dict_, scope, filterList=None): """ Private method to produce a formated variables list. @@ -1728,8 +1740,6 @@ expressions. The formated variables list (a list of tuples of 3 values) is returned. - @param keylist keys of the dictionary to be formatted - @type list of str @param dict_ the dictionary to be scanned @type dict @param scope 1 to filter using the globals filter, 0 using the locals @@ -1741,14 +1751,6 @@ Variables are only added to the list, if their type is not contained in the filter list. @type list of int - @param formatSequences flag indicating, that sequence or dictionary - variables should be formatted. If it is 0 (or false), just the - number of items contained in these variables is returned. - @type bool - @param maxSize maximum size the formatted value of a variable will - be shown. If it is bigger than that, a 'too big' indication will - be placed in the value field. - @type int @return A tuple consisting of a list of formatted variables. Each variable entry is a tuple of three elements, the variable name, its type and value. @@ -1761,8 +1763,14 @@ patternFilterObjects = self.globalsFilterObjects else: patternFilterObjects = self.localsFilterObjects + if type(dict_) == dict: + dict_ = dict_.items() - for key in keylist: + for key, value in dict_: + # no more elements available + if key == -2: + break + # filter based on the filter pattern matched = False for pat in patternFilterObjects: @@ -1773,9 +1781,7 @@ continue # filter hidden attributes (filter #0) - if 0 in filterList and str(key)[:2] == '__' and not ( - key == "___len___" and - DebugVariables.TooLargeAttribute in keylist): + if 0 in filterList and str(key)[:2] == '__': continue # special handling for '__builtins__' (it's way too big) @@ -1783,67 +1789,57 @@ rvalue = '<module __builtin__ (built-in)>' valtype = 'module' else: - value = dict_[key] valtypestr = str(type(value))[1:-1] _, valtype = valtypestr.split(' ', 1) valtype = valtype[1:-1] + # Strip 'instance' to be equal with Python 3 + if valtype == "instancemethod": + valtype = "method" + valtypename = type(value).__name__ - if valtype not in ConfigVarTypeStrings: - if valtype in ["numpy.ndarray", "array.array"]: + try: + if ConfigVarTypeStrings.index(valtype) in filterList: + continue + except ValueError: + if valtype == "classobj": + if ConfigVarTypeStrings.index( + 'instance') in filterList: + continue + elif valtype == "sip.methoddescriptor": + if ConfigVarTypeStrings.index('method') in filterList: + continue + elif valtype == "sip.enumtype": + if ConfigVarTypeStrings.index('class') in filterList: + continue + elif not valtype.startswith(("PySide.", "PySide2.")) and \ + (ConfigVarTypeStrings.index('other') in + filterList): + continue + elif valtype in ["numpy.ndarray", "array.array"]: if ConfigVarTypeStrings.index('list') in filterList: continue elif valtypename == "MultiValueDict": if ConfigVarTypeStrings.index('dict') in filterList: continue - elif valtype == "sip.methoddescriptor": - if ConfigVarTypeStrings.index( - 'method') in filterList: - continue - elif valtype == "sip.enumtype": - if ConfigVarTypeStrings.index('class') in filterList: - continue - elif ConfigVarTypeStrings.index('instance') in filterList: - continue if (not valtypestr.startswith('type ') and valtypename not in - ["ndarray", "MultiValueDict", "array"]): + ["ndarray", "MultiValueDict", "array"] and + not valtype.startswith(('PyQt5.', 'PyQt4.'))): valtype = valtypestr - else: - try: - # Strip 'instance' to be equal with Python 3 - if valtype == "instancemethod": - valtype = "method" - - if ConfigVarTypeStrings.index(valtype) in filterList: - continue - except ValueError: - if valtype == "classobj": - if ConfigVarTypeStrings.index( - 'instance') in filterList: - continue - elif valtype == "sip.methoddescriptor": - if ConfigVarTypeStrings.index( - 'method') in filterList: - continue - elif valtype == "sip.enumtype": - if ConfigVarTypeStrings.index('class') in \ - filterList: - continue - elif not valtype.startswith("PySide") and \ - (ConfigVarTypeStrings.index('other') in - filterList): - continue try: - if valtype in ['list', 'tuple', 'dict', 'set', - 'frozenset', 'array.array']: - if valtype == 'dict': - rvalue = "{0:d}".format(len(value.keys())) - else: - rvalue = "{0:d}".format(len(value)) + if valtype in ['list', 'tuple', 'dict', 'set', 'frozenset' + ]: + rvalue = "{0:d}".format(len(value)) + elif valtype == 'array.array': + rvalue = "{0:d}|{1}".format( + len(value), value.typecode) + elif valtype == 'collections.defaultdict': + rvalue = "{0:d}|{1}".format( + len(value), value.default_factory) elif valtype == "numpy.ndarray": - rvalue = "{0:d}".format(value.size) + rvalue = "x".join(str(x) for x in value.shape) elif valtypename == "MultiValueDict": rvalue = "{0:d}".format(len(value.keys())) valtype = "django.MultiValueDict" # shortened type @@ -1852,16 +1848,12 @@ if valtype.startswith('class') and \ rvalue[0] in ['{', '(', '[']: rvalue = "" - elif maxSize and len(rvalue) > maxSize: - rvalue = "@@TOO_BIG_TO_SHOW@@" + elif (rvalue.startswith("<class '") and + valtype.startswith(ConfigQtNames)): + rvalue = rvalue[8:-2] except Exception: rvalue = '' - if formatSequences: - if str(key) == key: - key = "'{0!s}'".format(key) - else: - key = str(key) varlist.append((key, valtype, rvalue)) return varlist