Continued improving the variable dumping of the debugger backends.

Fri, 16 Sep 2016 19:28:39 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 16 Sep 2016 19:28:39 +0200
changeset 5171
f1e9eebd5469
parent 5169
74e000797a93
child 5172
f35c7e0db572

Continued improving the variable dumping of the debugger backends.

DebugClients/Python2/DebugBase.py file | annotate | diff | comparison | revisions
DebugClients/Python2/DebugClientBase.py file | annotate | diff | comparison | revisions
DebugClients/Python2/DebugConfig.py file | annotate | diff | comparison | revisions
DebugClients/Python2/DebugVariables.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugClientBase.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugConfig.py file | annotate | diff | comparison | revisions
DebugClients/Python3/DebugVariables.py file | annotate | diff | comparison | revisions
Debugger/Config.py file | annotate | diff | comparison | revisions
Debugger/VariablesFilterDialog.py file | annotate | diff | comparison | revisions
Debugger/VariablesFilterDialog.ui file | annotate | diff | comparison | revisions
Debugger/VariablesViewer.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/DebugClients/Python2/DebugBase.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python2/DebugBase.py	Fri Sep 16 19:28:39 2016 +0200
@@ -886,8 +886,8 @@
             'DebugClientCapabilities.py',
             'DebugClientThreads.py',
             'DebugConfig.py', 'DebugThread.py',
-            'DebugUtilities.py', 'FlexCompleter.py',
-            'PyProfile.py'] or \
+            'DebugUtilities.py', 'DebugVariables.py',
+            'FlexCompleter.py', 'PyProfile.py'] or \
            os.path.dirname(fn).endswith("coverage"):
             return True
 
--- a/DebugClients/Python2/DebugClientBase.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python2/DebugClientBase.py	Fri Sep 16 19:28:39 2016 +0200
@@ -27,6 +27,7 @@
 from DebugConfig import ConfigVarTypeStrings
 from FlexCompleter import Completer
 from DebugUtilities import prepareJsonCommand
+import DebugVariables
 
 
 DebugClientInstance = None
@@ -164,6 +165,9 @@
     """
     clientCapabilities = DebugClientCapabilities.HasAll
     
+    # keep these in sync with VariablesViewer.VariableItem.Indicators
+    Indicators = ("()", "[]", "{:}", "{}")         # __IGNORE_WARNING__
+    
     def __init__(self):
         """
         Constructor
@@ -1431,217 +1435,25 @@
         varlist = []
         
         if scope != -1:
-            # search the correct dictionary
-            i = 0
-            rvar = var[:]
-            dictkeys = None
-            obj = None
-            isDict = False
-            formatSequences = False
-            access = ""
-            oaccess = ""
-            odict = dict
-            
-            qtVariable = False
-            qvar = None
-            qvtype = ""
-            
-            while i < len(var):
-                if len(dict):
-                    udict = dict
-                ndict = {}
-                # this has to be in line with VariablesViewer.indicators
-                if var[i][-2:] in ["[]", "()", "{}"]:   # __IGNORE_WARNING__
-                    if i + 1 == len(var):
-                        if var[i][:-2] == '...':
-                            dictkeys = [var[i - 1]]
-                        else:
-                            dictkeys = [var[i][:-2]]
-                        formatSequences = True
-                        if not access and not oaccess:
-                            if var[i][:-2] == '...':
-                                access = '["%s"]' % var[i - 1]
-                                dict = odict
-                            else:
-                                access = '["%s"]' % var[i][:-2]
-                        else:
-                            if var[i][:-2] == '...':
-                                if oaccess:
-                                    access = oaccess
-                                else:
-                                    access = '%s[%s]' % (access, var[i - 1])
-                                dict = odict
-                            else:
-                                if oaccess:
-                                    access = '%s[%s]' % (oaccess, var[i][:-2])
-                                    oaccess = ''
-                                else:
-                                    access = '%s[%s]' % (access, var[i][:-2])
-                        if var[i][-2:] == "{}":         # __IGNORE_WARNING__
-                            isDict = True
-                        break
-                    else:
-                        if not access:
-                            if var[i][:-2] == '...':
-                                access = '["%s"]' % var[i - 1]
-                                dict = odict
-                            else:
-                                access = '["%s"]' % var[i][:-2]
-                        else:
-                            if var[i][:-2] == '...':
-                                access = '%s[%s]' % (access, var[i - 1])
-                                dict = odict
-                            else:
-                                if oaccess:
-                                    access = '%s[%s]' % (oaccess, var[i][:-2])
-                                    oaccess = ''
-                                else:
-                                    access = '%s[%s]' % (access, var[i][:-2])
+            variable = dict
+            for attribute in var:
+                attribute = self.__extractIndicators(attribute)[0]
+                typeObject, typeName, typeStr, resolver = \
+                    DebugVariables.getType(variable)
+                if resolver:
+                    variable = resolver.resolve(variable, attribute)
                 else:
-                    if access:
-                        if oaccess:
-                            access = '%s[%s]' % (oaccess, var[i])
-                        else:
-                            access = '%s[%s]' % (access, var[i])
-                        if var[i - 1][:-2] == '...':
-                            oaccess = access
-                        else:
-                            oaccess = ''
-                        try:
-                            exec 'mdict = dict%s.__dict__' % access
-                            ndict.update(mdict)     # __IGNORE_WARNING__
-                            exec 'obj = dict%s' % access
-                            if "PyQt4." in str(type(obj)) or \
-                                    "PyQt5." in str(type(obj)):
-                                qtVariable = True
-                                qvar = obj
-                                qvtype = ("%s" % type(qvar))[1:-1]\
-                                    .split()[1][1:-1]
-                        except Exception:
-                            pass
-                        try:
-                            exec 'mcdict = dict%s.__class__.__dict__' % access
-                            ndict.update(mcdict)     # __IGNORE_WARNING__
-                            if mdict and "sipThis" not in mdict.keys():  # __IGNORE_WARNING__
-                                del rvar[0:2]
-                                access = ""
-                        except Exception:
-                            pass
-                        try:
-                            cdict = {}
-                            exec 'slv = dict%s.__slots__' % access
-                            for v in slv:   # __IGNORE_WARNING__
-                                try:
-                                    exec 'cdict[v] = dict%s.%s' % (access, v)
-                                except Exception:
-                                    pass
-                            ndict.update(cdict)
-                            exec 'obj = dict%s' % access
-                            access = ""
-                            if "PyQt4." in str(type(obj)) or \
-                                    "PyQt5." in str(type(obj)):
-                                qtVariable = True
-                                qvar = obj
-                                qvtype = ("%s" % type(qvar))[1:-1]\
-                                    .split()[1][1:-1]
-                        except Exception:
-                            pass
-                    else:
-                        try:
-                            ndict.update(dict[var[i]].__dict__)
-                            ndict.update(dict[var[i]].__class__.__dict__)
-                            del rvar[0]
-                            obj = dict[var[i]]
-                            if "PyQt4." in str(type(obj)) or \
-                                    "PyQt5." in str(type(obj)):
-                                qtVariable = True
-                                qvar = obj
-                                qvtype = ("%s" % type(qvar))[1:-1]\
-                                    .split()[1][1:-1]
-                        except Exception:
-                            pass
-                        try:
-                            cdict = {}
-                            slv = dict[var[i]].__slots__
-                            for v in slv:
-                                try:
-                                    exec 'cdict[v] = dict[var[i]].%s' % v
-                                except Exception:
-                                    pass
-                            ndict.update(cdict)
-                            obj = dict[var[i]]
-                            if "PyQt4." in str(type(obj)) or \
-                                    "PyQt5." in str(type(obj)):
-                                qtVariable = True
-                                qvar = obj
-                                qvtype = ("%s" % type(qvar))[1:-1]\
-                                    .split()[1][1:-1]
-                        except Exception:
-                            pass
-                    odict = dict
-                    dict = ndict
-                i += 1
-            
-            if qtVariable:
-                vlist = self.__formatQtVariable(qvar, qvtype)
-            elif ("sipThis" in dict.keys() and len(dict) == 1) or \
-                    (len(dict) == 0 and len(udict) > 0):
-                if access:
-                    exec 'qvar = udict%s' % access
-                # this has to be in line with VariablesViewer.indicators
-                elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]:   # __IGNORE_WARNING__
-                    exec 'qvar = udict["%s"][%s]' % (rvar[0][:-2], rvar[1])
-                else:
-                    qvar = udict[var[-1]]
-                qvtype = ("%s" % type(qvar))[1:-1].split()[1][1:-1]
-                if qvtype.startswith(("PyQt4", "PyQt5")):
-                    vlist = self.__formatQtVariable(qvar, qvtype)
-                else:
-                    vlist = []
-            else:
-                qtVariable = False
-                if len(dict) == 0 and len(udict) > 0:
-                    if access:
-                        exec 'qvar = udict%s' % access
-                    # this has to be in line with VariablesViewer.indicators
-                    elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]:   # __IGNORE_WARNING__
-                        exec 'qvar = udict["%s"][%s]' % (rvar[0][:-2], rvar[1])
-                    else:
-                        qvar = udict[var[-1]]
-                    qvtype = ("%s" % type(qvar))[1:-1].split()[1][1:-1]
-                    if qvtype.startswith(("PyQt4", "PyQt5")):
-                        qtVariable = True
-                
-                if qtVariable:
-                    vlist = self.__formatQtVariable(qvar, qvtype)
-                else:
-                    # format the dictionary found
-                    if dictkeys is None:
-                        dictkeys = dict.keys()
-                    else:
-                        # treatment for sequences and dictionaries
-                        if access:
-                            exec "dict = dict%s" % access
-                        else:
-                            dict = dict[dictkeys[0]]
-                        if isDict:
-                            dictkeys = dict.keys()
-                        else:
-                            dictkeys = range(len(dict))
-                    vlist = self.__formatVariablesList(
-                        dictkeys, dict, scope, filter, formatSequences)
-            varlist.extend(vlist)
-        
-            if obj is not None and not formatSequences:
-                try:
-                    if unicode(repr(obj)).startswith('{'):
-                        varlist.append(('...', 'dict', "%d" % len(obj.keys())))
-                    elif unicode(repr(obj)).startswith('['):
-                        varlist.append(('...', 'list', "%d" % len(obj)))
-                    elif unicode(repr(obj)).startswith('('):
-                        varlist.append(('...', 'tuple', "%d" % len(obj)))
-                except Exception:
-                    pass
+                    break
+            typeObject, typeName, typeStr, resolver = \
+                DebugVariables.getType(variable)
+            if typeStr.startswith(("PyQt5.", "PyQt4.")):
+                vlist = self.__formatQtVariable(variable, typeName)
+                varlist.extend(vlist)
+            elif resolver:
+                dict = resolver.getDictionary(variable)
+                vlist = self.__formatVariablesList(
+                    list(dict.keys()), dict, scope, filter)
+                varlist.extend(vlist)
         
         self.sendJsonCommand("ResponseVariable", {
             "scope": scope,
@@ -1649,17 +1461,32 @@
             "variables": varlist,
         })
         
-    def __formatQtVariable(self, value, vtype):
+    def __extractIndicators(self, var):
+        """
+        Private method to extract the indicator string from a variable text.
+        
+        @param var variable text
+        @type str
+        @return tuple containing the variable text without indicators and the
+            indicator string
+        @rtype tuple of two str
         """
-        Private method to produce a formated output of a simple Qt4/Qt5 type.
+        for indicator in DebugClientBase.Indicators:
+            if var.endswith(indicator):
+                return var[:-len(indicator)], indicator
+        
+        return var, ""
         
-        @param value variable to be formated
-        @param vtype type of the variable to be formatted (string)
+    def __formatQtVariable(self, value, qttype):
+        """
+        Private method to produce a formatted output of a simple Qt4/Qt5 type.
+        
+        @param value variable to be formatted
+        @param qttype type of the Qt variable to be formatted (string)
         @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.
         """
-        qttype = vtype.split('.')[-1]
         varlist = []
         if qttype == 'QChar':
             varlist.append(("", "QChar", "%s" % unichr(value.unicode())))
@@ -1782,7 +1609,7 @@
         return varlist
         
     def __formatVariablesList(self, keylist, dict, scope, filter=[],
-                              formatSequences=0):
+                              formatSequences=False):
         """
         Private method to produce a formated variables list.
         
@@ -1825,7 +1652,9 @@
                 continue
             
             # filter hidden attributes (filter #0)
-            if 0 in filter and unicode(key)[:2] == '__':
+            if 0 in filter and unicode(key)[:2] == '__' and not (
+                key == "___len___" and
+                    DebugVariables.TooLargeAttribute in keylist):
                 continue
             
             # special handling for '__builtins__' (it's way too big)
@@ -1835,7 +1664,7 @@
             else:
                 value = dict[key]
                 valtypestr = ("%s" % type(value))[1:-1]
-                    
+                
                 if valtypestr.split(' ', 1)[0] == 'class':
                     # handle new class type of python 2.2+
                     if ConfigVarTypeStrings.index('instance') in filter:
@@ -1853,7 +1682,7 @@
                                 continue
                         elif valtype == "sip.methoddescriptor":
                             if ConfigVarTypeStrings.index(
-                                    'instance method') in filter:
+                                    'method') in filter:
                                 continue
                         elif valtype == "sip.enumtype":
                             if ConfigVarTypeStrings.index('class') in filter:
@@ -1863,7 +1692,8 @@
                             continue
                     
                 try:
-                    if valtype not in ['list', 'tuple', 'dict']:
+                    if valtype not in ['list', 'tuple', 'dict', 'set',
+                                       'frozenset']:
                         rvalue = repr(value)
                         if valtype.startswith('class') and \
                            rvalue[0] in ['{', '(', '[']:
--- a/DebugClients/Python2/DebugConfig.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python2/DebugConfig.py	Fri Sep 16 19:28:39 2016 +0200
@@ -7,15 +7,18 @@
 Module defining type strings for the different Python types.
 """
 
+#
+# Keep this list in sync with Debugger.Config.ConfigVarTypeFilters
+#
 ConfigVarTypeStrings = [
     '__', 'NoneType', 'type',
     'bool', 'int', 'long', 'float', 'complex',
     'str', 'unicode', 'tuple', 'list',
     'dict', 'dict-proxy', 'set', 'file', 'xrange',
     'slice', 'buffer', 'class', 'instance',
-    'instance method', 'property', 'generator',
+    'method', 'property', 'generator',
     'function', 'builtin_function_or_method', 'code', 'module',
-    'ellipsis', 'traceback', 'frame', 'other'
+    'ellipsis', 'traceback', 'frame', 'other', 'frozenset'
 ]
 
 #
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DebugClients/Python2/DebugVariables.py	Fri Sep 16 19:28:39 2016 +0200
@@ -0,0 +1,364 @@
+# -*- 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 = "%s (ID:%s)" % (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: FileType = Python2
+# eflag: noqa = M601,M702
--- a/DebugClients/Python3/DebugClientBase.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python3/DebugClientBase.py	Fri Sep 16 19:28:39 2016 +0200
@@ -138,6 +138,9 @@
     """
     clientCapabilities = DebugClientCapabilities.HasAll
     
+    # keep these in sync with VariablesViewer.VariableItem.Indicators
+    Indicators = ("()", "[]", "{:}", "{}")         # __IGNORE_WARNING__
+    
     def __init__(self):
         """
         Constructor
@@ -1433,8 +1436,7 @@
         if scope != -1:
             variable = dict
             for attribute in var:
-                if attribute[-2:] in ["[]", "()", "{}"]:  # __IGNORE_WARNING__
-                    attribute = attribute[:-2]
+                attribute = self.__extractIndicators(attribute)[0]
                 typeObject, typeName, typeStr, resolver = \
                     DebugVariables.getType(variable)
                 if resolver:
@@ -1458,6 +1460,22 @@
             "variables": varlist,
         })
         
+    def __extractIndicators(self, var):
+        """
+        Private method to extract the indicator string from a variable text.
+        
+        @param var variable text
+        @type str
+        @return tuple containing the variable text without indicators and the
+            indicator string
+        @rtype tuple of two str
+        """
+        for indicator in DebugClientBase.Indicators:
+            if var.endswith(indicator):
+                return var[:-len(indicator)], indicator
+        
+        return var, ""
+        
     def __formatQtVariable(self, value, qttype):
         """
         Private method to produce a formatted output of a simple Qt4/Qt5 type.
@@ -1640,7 +1658,9 @@
                 continue
             
             # filter hidden attributes (filter #0)
-            if 0 in filter and str(key)[:2] == '__':
+            if 0 in filter and str(key)[:2] == '__' and not (
+                key == "___len___" and
+                    DebugVariables.TooLargeAttribute in keylist):
                 continue
             
             # special handling for '__builtins__' (it's way too big)
@@ -1684,7 +1704,8 @@
                             continue
                 
                 try:
-                    if valtype not in ['list', 'tuple', 'dict']:
+                    if valtype not in ['list', 'tuple', 'dict', 'set',
+                                       'frozenset']:
                         rvalue = repr(value)
                         if valtype.startswith('class') and \
                            rvalue[0] in ['{', '(', '[']:
--- a/DebugClients/Python3/DebugConfig.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python3/DebugConfig.py	Fri Sep 16 19:28:39 2016 +0200
@@ -7,6 +7,9 @@
 Module defining type strings for the different Python types.
 """
 
+#
+# Keep this list in sync with Debugger.Config.ConfigVarTypeFilters
+#
 ConfigVarTypeStrings = [
     '__', 'NoneType', 'type',
     'bool', 'int', 'long', 'float', 'complex',
@@ -15,7 +18,7 @@
     'slice', 'buffer', 'class', 'instance',
     'method', 'property', 'generator',
     'function', 'builtin_function_or_method', 'code', 'module',
-    'ellipsis', 'traceback', 'frame', 'other'
+    'ellipsis', 'traceback', 'frame', 'other', 'frozenset'
 ]
 
 #
--- a/DebugClients/Python3/DebugVariables.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/DebugClients/Python3/DebugVariables.py	Fri Sep 16 19:28:39 2016 +0200
@@ -12,7 +12,7 @@
 #
 
 MaxItemsToHandle = 300
-TooLargeMessage = ("Too large to show contents. Max items to show: " + 
+TooLargeMessage = ("Too large to show contents. Max items to show: " +
                    str(MaxItemsToHandle))
 TooLargeAttribute = "Too large to be handled."
 
@@ -20,6 +20,7 @@
 ## Classes implementing resolvers for various compund types
 ############################################################
 
+
 class BaseResolver(object):
     """
     Base class of the resolver class tree.
@@ -34,7 +35,9 @@
         @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):
@@ -45,7 +48,9 @@
         @type any
         @return dictionary containing the variable attributes
         @rtype dict
-        """
+        @exception NotImplementedError raised to indicate a missing
+            implementation
+        """     # __IGNORE_WARNING_D235__
         raise NotImplementedError
 
 
@@ -105,18 +110,16 @@
         @return value of the attribute
         @rtype any
         """
-        if attribute in ('__len__', TooLargeAttribute):
+        if attribute in ('___len___', TooLargeAttribute):
             return None
         
-        if "(" not in attribute:
+        if "(ID:" 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])
+        expectedID = int(attribute.split("(ID:")[-1][:-1])
         for key, value in var.items():
             if id(key) == expectedID:
                 return value
@@ -124,6 +127,14 @@
         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:
@@ -142,13 +153,13 @@
         count = 0
         for key, value in var.items():
             count += 1
-            key = "{0} ({1})".format(self.__keyToStr(key), id(key))
+            key = "{0} (ID:{1})".format(self.__keyToStr(key), id(key))
             d[key] = value
             if count > MaxItemsToHandle:
                 d[TooLargeAttribute] = TooLargeMessage
                 break
         
-        d["__len__"] = len(var)
+        d["___len___"] = len(var)
         
         # in case it has additional fields
         additionals = defaultResolver.getDictionary(var)
@@ -172,7 +183,7 @@
         @return value of the attribute
         @rtype any
         """
-        if attribute in ('__len__', TooLargeAttribute):
+        if attribute in ('___len___', TooLargeAttribute):
             return None
 
         try:
@@ -189,18 +200,74 @@
         @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
+            d[str(count)] = value
             count += 1
             if count > MaxItemsToHandle:
                 d[TooLargeAttribute] = TooLargeMessage
                 break
         
-        d["__len__"] = length
+        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)
@@ -212,8 +279,8 @@
 defaultResolver = DefaultResolver()
 dictResolver = DictResolver()
 listResolver = ListResolver()
+setResolver = SetResolver()
 
-# 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
@@ -225,9 +292,10 @@
 
 _TypeMap = None
 
+
 def _initTypeMap():
     """
-    Protected function to initialize the type map
+    Protected function to initialize the type map.
     """
     global _TypeMap
     
@@ -252,6 +320,16 @@
     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):
     """
@@ -280,3 +358,6 @@
             resolver = defaultResolver
     
     return typeObject, typeName, typeStr, resolver
+
+#
+# eflag: noqa = M702
--- a/Debugger/Config.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/Debugger/Config.py	Fri Sep 16 19:28:39 2016 +0200
@@ -9,51 +9,80 @@
 
 from __future__ import unicode_literals
 
-try:
-    from PyQt5.QtCore import QT_TRANSLATE_NOOP
-    
-    # Variables type definition
-    ConfigVarTypeDispStrings = [
-        QT_TRANSLATE_NOOP('Variable Types', 'Hidden Attributes'),
-        QT_TRANSLATE_NOOP('Variable Types', 'None'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Type'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Boolean'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Integer'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Long Integer'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Float'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Complex'),
-        QT_TRANSLATE_NOOP('Variable Types', 'String'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Unicode String'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Tuple'),
-        QT_TRANSLATE_NOOP('Variable Types', 'List/Array'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Dictionary/Hash/Map'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Dictionary Proxy'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Set'),
-        QT_TRANSLATE_NOOP('Variable Types', 'File'),
-        QT_TRANSLATE_NOOP('Variable Types', 'X Range'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Slice'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Buffer'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Class'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Class Instance'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Class Method'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Class Property'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Generator'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Function'),
+# TODO: change these to dictionaries with keys according to DebugConfig.py
+from PyQt5.QtCore import QT_TRANSLATE_NOOP
+
+# Variables type definition
+ConfigVarTypeDispStrings = {
+    '__': QT_TRANSLATE_NOOP('Variable Types', 'Hidden Attributes'),
+    'NoneType': QT_TRANSLATE_NOOP('Variable Types', 'None'),
+    'type': QT_TRANSLATE_NOOP('Variable Types', 'Type'),
+    'bool': QT_TRANSLATE_NOOP('Variable Types', 'Boolean'),
+    'int': QT_TRANSLATE_NOOP('Variable Types', 'Integer'),
+    'long': QT_TRANSLATE_NOOP('Variable Types', 'Long Integer'),
+    'float': QT_TRANSLATE_NOOP('Variable Types', 'Float'),
+    'complex': QT_TRANSLATE_NOOP('Variable Types', 'Complex'),
+    'str': QT_TRANSLATE_NOOP('Variable Types', 'String'),
+    'unicode': QT_TRANSLATE_NOOP('Variable Types', 'Unicode String'),
+    'tuple': QT_TRANSLATE_NOOP('Variable Types', 'Tuple'),
+    'list': QT_TRANSLATE_NOOP('Variable Types', 'List/Array'),
+    'dict': QT_TRANSLATE_NOOP('Variable Types', 'Dictionary/Hash/Map'),
+    'dict-proxy': QT_TRANSLATE_NOOP('Variable Types', 'Dictionary Proxy'),
+    'set': QT_TRANSLATE_NOOP('Variable Types', 'Set'),
+    'frozenset': QT_TRANSLATE_NOOP('Variable Types', 'Frozen Set'),
+    'file': QT_TRANSLATE_NOOP('Variable Types', 'File'),
+    'xrange': QT_TRANSLATE_NOOP('Variable Types', 'X Range'),
+    'slice': QT_TRANSLATE_NOOP('Variable Types', 'Slice'),
+    'buffer': QT_TRANSLATE_NOOP('Variable Types', 'Buffer'),
+    'class': QT_TRANSLATE_NOOP('Variable Types', 'Class'),
+    'instance': QT_TRANSLATE_NOOP('Variable Types', 'Class Instance'),
+    'method': QT_TRANSLATE_NOOP('Variable Types', 'Class Method'),
+    'property': QT_TRANSLATE_NOOP('Variable Types', 'Class Property'),
+    'generator': QT_TRANSLATE_NOOP('Variable Types', 'Generator'),
+    'function': QT_TRANSLATE_NOOP('Variable Types', 'Function'),
+    'builtin_function_or_method':
         QT_TRANSLATE_NOOP('Variable Types', 'Builtin Function'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Code'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Module'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Ellipsis'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Traceback'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Frame'),
-        QT_TRANSLATE_NOOP('Variable Types', 'Other')
-    ]
-except ImportError:
-    # Variables type definition (for non-Qt only)
-    ConfigVarTypeDispStrings = [
-        'Hidden Attributes', 'None', 'Type', 'Boolean', 'Integer',
-        'Long Integer', 'Float', 'Complex', 'String', 'Unicode String',
-        'Tuple', 'List/Array', 'Dictionary/Hash/Map', 'Dictionary Proxy',
-        'Set', 'File', 'X Range', 'Slice', 'Buffer', 'Class',
-        'Class Instance', 'Class Method', 'Class Property', 'Generator',
-        'Function', 'Builtin Function', 'Code', 'Module', 'Ellipsis',
-        'Traceback', 'Frame', 'Other']
+    'code': QT_TRANSLATE_NOOP('Variable Types', 'Code'),
+    'module': QT_TRANSLATE_NOOP('Variable Types', 'Module'),
+    'ellipsis': QT_TRANSLATE_NOOP('Variable Types', 'Ellipsis'),
+    'traceback': QT_TRANSLATE_NOOP('Variable Types', 'Traceback'),
+    'frame': QT_TRANSLATE_NOOP('Variable Types', 'Frame'),
+    'other': QT_TRANSLATE_NOOP('Variable Types', 'Other'),
+}
+
+
+ConfigVarTypeFilters = {
+    '__': 0,
+    'NoneType': 1,
+    'type': 2,
+    'bool': 3,
+    'int': 4,
+    'long': 5,
+    'float': 6,
+    'complex': 7,
+    'str': 8,
+    'unicode': 9,
+    'tuple': 10,
+    'list': 11,
+    'dict': 12,
+    'dict-proxy': 13,
+    'set': 14,
+    'file': 15,
+    'xrange': 16,
+    'slice': 17,
+    'buffer': 18,
+    'class': 19,
+    'instance': 20,
+    'method': 21,
+    'property': 22,
+    'generator': 23,
+    'function': 24,
+    'builtin_function_or_method': 25,
+    'code': 26,
+    'module': 27,
+    'ellipsis': 28,
+    'traceback': 29,
+    'frame': 30,
+    'other': 31,
+    'frozenset': 32,
+}
--- a/Debugger/VariablesFilterDialog.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/Debugger/VariablesFilterDialog.py	Fri Sep 16 19:28:39 2016 +0200
@@ -9,9 +9,10 @@
 
 from __future__ import unicode_literals
 
-from PyQt5.QtWidgets import QDialog, QDialogButtonBox
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem
 
-from Debugger.Config import ConfigVarTypeDispStrings
+from Debugger.Config import ConfigVarTypeDispStrings, ConfigVarTypeFilters
 import Preferences
 
 from .Ui_VariablesFilterDialog import Ui_VariablesFilterDialog
@@ -41,19 +42,15 @@
         self.defaultButton = self.buttonBox.addButton(
             self.tr("Save Default"), QDialogButtonBox.ActionRole)
         
-        lDefaultFilter, gDefaultFilter = Preferences.getVarFilters()
+        #populate the list widgets and set the default selection
+        for widget in self.localsList, self.globalsList:
+            for varType, varTypeStr in ConfigVarTypeDispStrings.items():
+                itm = QListWidgetItem(self.tr(varTypeStr), widget)
+                itm.setData(Qt.UserRole, ConfigVarTypeFilters[varType])
+                widget.addItem(itm)
         
-        #populate the listboxes and set the default selection
-        for lb in self.localsList, self.globalsList:
-            for ts in ConfigVarTypeDispStrings:
-                lb.addItem(self.tr(ts))
-                
-        for filterIndex in lDefaultFilter:
-            itm = self.localsList.item(filterIndex)
-            itm.setSelected(True)
-        for filterIndex in gDefaultFilter:
-            itm = self.globalsList.item(filterIndex)
-            itm.setSelected(True)
+        lDefaultFilter, gDefaultFilter = Preferences.getVarFilters()
+        self.setSelection(lDefaultFilter, gDefaultFilter)
 
     def getSelection(self):
         """
@@ -63,17 +60,18 @@
             locals variables filter, the second the globals variables filter.
         """
         lList = []
-        gList = []
         for row in range(self.localsList.count()):
             itm = self.localsList.item(row)
             if itm.isSelected():
-                lList.append(row)
+                lList.append(itm.data(Qt.UserRole))
+        
+        gList = []
         for row in range(self.globalsList.count()):
             itm = self.globalsList.item(row)
             if itm.isSelected():
-                gList.append(row)
+                gList.append(itm.data(Qt.UserRole))
         return (lList, gList)
-        
+    
     def setSelection(self, lList, gList):
         """
         Public slot to set the current selection.
@@ -83,10 +81,11 @@
         """
         for row in range(self.localsList.count()):
             itm = self.localsList.item(row)
-            itm.setSelected(row in lList)
+            itm.setSelected(itm.data(Qt.UserRole) in lList)
+        
         for row in range(self.globalsList.count()):
             itm = self.globalsList.item(row)
-            itm.setSelected(row in gList)
+            itm.setSelected(itm.data(Qt.UserRole) in gList)
 
     def on_buttonBox_clicked(self, button):
         """
--- a/Debugger/VariablesFilterDialog.ui	Wed Sep 14 20:08:16 2016 +0200
+++ b/Debugger/VariablesFilterDialog.ui	Fri Sep 16 19:28:39 2016 +0200
@@ -1,8 +1,9 @@
-<ui version="4.0" >
- <author>Detlev Offenbach &lt;detlev@die-offenbachs.de></author>
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>Detlev Offenbach &lt;detlev@die-offenbachs.de&gt;</author>
  <class>VariablesFilterDialog</class>
- <widget class="QDialog" name="VariablesFilterDialog" >
-  <property name="geometry" >
+ <widget class="QDialog" name="VariablesFilterDialog">
+  <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
@@ -10,78 +11,90 @@
     <height>338</height>
    </rect>
   </property>
-  <property name="windowTitle" >
+  <property name="windowTitle">
    <string>Variables Type Filter</string>
   </property>
-  <property name="whatsThis" >
-   <string>&lt;b>Filter Dialog&lt;/b>
-&lt;p> This dialog gives the user the possibility to select what kind of variables should &lt;b>not&lt;/b> be shown during a debugging session.&lt;/p></string>
+  <property name="whatsThis">
+   <string>&lt;b&gt;Filter Dialog&lt;/b&gt;
+&lt;p&gt; This dialog gives the user the possibility to select what kind of variables should &lt;b&gt;not&lt;/b&gt; be shown during a debugging session.&lt;/p&gt;</string>
   </property>
-  <property name="sizeGripEnabled" >
+  <property name="sizeGripEnabled">
    <bool>true</bool>
   </property>
-  <layout class="QGridLayout" >
-   <item row="2" column="0" colspan="2" >
-    <widget class="QDialogButtonBox" name="buttonBox" >
-     <property name="orientation" >
+  <layout class="QGridLayout">
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
-     <property name="standardButtons" >
+     <property name="standardButtons">
       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
      </property>
     </widget>
    </item>
-   <item row="0" column="0" >
-    <widget class="QLabel" name="localsLabel" >
-     <property name="text" >
+   <item row="0" column="0">
+    <widget class="QLabel" name="localsLabel">
+     <property name="text">
       <string>&amp;Locals Filter</string>
      </property>
-     <property name="buddy" >
+     <property name="buddy">
       <cstring>localsList</cstring>
      </property>
     </widget>
    </item>
-   <item row="0" column="1" >
-    <widget class="QLabel" name="globalsLabel" >
-     <property name="text" >
+   <item row="0" column="1">
+    <widget class="QLabel" name="globalsLabel">
+     <property name="text">
       <string>&amp;Globals Filter</string>
      </property>
-     <property name="buddy" >
+     <property name="buddy">
       <cstring>globalsList</cstring>
      </property>
     </widget>
    </item>
-   <item row="1" column="0" >
-    <widget class="QListWidget" name="localsList" >
-     <property name="toolTip" >
+   <item row="1" column="0">
+    <widget class="QListWidget" name="localsList">
+     <property name="toolTip">
       <string>Locals Filter List</string>
      </property>
-     <property name="whatsThis" >
-      <string>&lt;b>Locals Filter List&lt;/b>
-&lt;p>Select the variable types you want to be filtered out of the locals variables list.&lt;/p&lt;</string>
+     <property name="whatsThis">
+      <string>&lt;b&gt;Locals Filter List&lt;/b&gt;
+&lt;p&gt;Select the variable types you want to be filtered out of the locals variables list.&lt;/p&lt;</string>
      </property>
-     <property name="selectionMode" >
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
       <enum>QAbstractItemView::ExtendedSelection</enum>
      </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
     </widget>
    </item>
-   <item row="1" column="1" >
-    <widget class="QListWidget" name="globalsList" >
-     <property name="toolTip" >
+   <item row="1" column="1">
+    <widget class="QListWidget" name="globalsList">
+     <property name="toolTip">
       <string>Globals Filter List</string>
      </property>
-     <property name="whatsThis" >
-      <string>&lt;b>Globals Filter List&lt;/b>
-&lt;p>Select the variable types you want to be filtered out of the globals variables list.&lt;/p&lt;</string>
+     <property name="whatsThis">
+      <string>&lt;b&gt;Globals Filter List&lt;/b&gt;
+&lt;p&gt;Select the variable types you want to be filtered out of the globals variables list.&lt;/p&lt;</string>
      </property>
-     <property name="selectionMode" >
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionMode">
       <enum>QAbstractItemView::ExtendedSelection</enum>
      </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
     </widget>
    </item>
   </layout>
  </widget>
- <layoutdefault spacing="6" margin="11" />
+ <layoutdefault spacing="6" margin="11"/>
  <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
  <tabstops>
   <tabstop>localsList</tabstop>
@@ -95,11 +108,11 @@
    <receiver>VariablesFilterDialog</receiver>
    <slot>accept()</slot>
    <hints>
-    <hint type="sourcelabel" >
+    <hint type="sourcelabel">
      <x>14</x>
      <y>319</y>
     </hint>
-    <hint type="destinationlabel" >
+    <hint type="destinationlabel">
      <x>15</x>
      <y>332</y>
     </hint>
@@ -111,11 +124,11 @@
    <receiver>VariablesFilterDialog</receiver>
    <slot>reject()</slot>
    <hints>
-    <hint type="sourcelabel" >
+    <hint type="sourcelabel">
      <x>84</x>
      <y>317</y>
     </hint>
-    <hint type="destinationlabel" >
+    <hint type="destinationlabel">
      <x>84</x>
      <y>336</y>
     </hint>
--- a/Debugger/VariablesViewer.py	Wed Sep 14 20:08:16 2016 +0200
+++ b/Debugger/VariablesViewer.py	Fri Sep 16 19:28:39 2016 +0200
@@ -16,12 +16,9 @@
 from PyQt5.QtCore import Qt, QRegExp, qVersion, QCoreApplication
 from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QAbstractItemView, \
     QMenu
-from PyQt5.QtGui import QTextDocument   # __IGNORE_WARNING__
 
 from E5Gui.E5Application import e5App
 
-from DebugClients.Python3.DebugConfig import ConfigVarTypeStrings
-    
 from .Config import ConfigVarTypeDispStrings
 
 import Preferences
@@ -32,6 +29,16 @@
     """
     Class implementing the data structure for variable items.
     """
+    Indicators = ("()", "[]", "{:}", "{}")      # __IGNORE_WARNING_M613__
+    Type2Indicators = {
+        # Python types
+        'list': '[]',
+        'tuple': '()',
+        'dict': '{:}',                          # __IGNORE_WARNING_M613__
+        'set': '{}',                            # __IGNORE_WARNING_M613__
+        'frozenset': '{}',                      # __IGNORE_WARNING_M613__
+    }
+    
     def __init__(self, parent, dvar, dvalue, dtype):
         """
         Constructor
@@ -41,6 +48,8 @@
         @param dvalue value string (string)
         @param dtype type string (string)
         """
+        dvar, self.__varID = VariableItem.extractId(dvar)
+        
         self.__value = dvalue
         if len(dvalue) > 2048:     # 1024 * 2
             dvalue = QCoreApplication.translate(
@@ -77,7 +86,71 @@
         @return value of the item (string)
         """
         return self.__value
+    
+    def getId(self):
+        """
+        Public method to get the ID string.
         
+        @return ID string
+        @rtype str
+        """
+        return self.__varID
+    
+    @classmethod
+    def extractId(cls, var):
+        """
+        Class method to extract the ID string from a variable text.
+        
+        @param var variable text
+        @type str
+        @return tuple containing the variable text without ID and the ID string
+        @rtype tuple of two str
+        """
+        if " (ID:" in var:
+            dvar, varID = var.rsplit(None, 1)
+            if varID.endswith(VariableItem.Indicators):
+                varID, indicators = VariableItem.extractIndicators(varID)
+                dvar += indicators
+        else:
+            dvar = var
+            varID = None
+        
+        return dvar, varID
+    
+    @classmethod
+    def extractIndicators(cls, var):
+        """
+        Class method to extract the indicator string from a variable text.
+        
+        @param var variable text
+        @type str
+        @return tuple containing the variable text without indicators and the
+            indicator string
+        @rtype tuple of two str
+        """
+        for indicator in VariableItem.Indicators:
+            if var.endswith(indicator):
+                return var[:-len(indicator)], indicator
+        
+        return var, ""
+    
+    def _buildKey(self):
+        """
+        Protected method to build the access key for the variable.
+        
+        @return access key
+        @type str
+        """
+        indicators = ""
+        txt = self.text(0)
+        if txt.endswith(VariableItem.Indicators):
+            txt, indicators = VariableItem.extractIndicators(txt)
+        if self.__varID:
+            txt = "{0} {1}{2}".format(txt, self.__varID, indicators)
+        else:
+            txt = "{0}{1}".format(txt, indicators)
+        return txt
+    
     def data(self, column, role):
         """
         Public method to return the data for the requested role.
@@ -156,12 +229,12 @@
         self.deleteChildren()
         self.populated = True
         
-        pathlist = [self.text(0)]
+        pathlist = [self._buildKey()]
         par = self.parent()
         
         # step 1: get a pathlist up to the requested variable
         while par is not None:
-            pathlist.insert(0, par.text(0))
+            pathlist.insert(0, par._buildKey())
             par = par.parent()
         
         # step 2: request the variable from the debugger
@@ -221,9 +294,9 @@
         element 2 will have a key of '000002' and appear before
         element 10 with a key of '000010'
         """
-        col0Str = self.text(0)[:-2]  # strip off [], () or {}
-        indicator = self.text(0)[-2:]
-        self.setText(0, "{0:6d}{1}".format(int(col0Str), indicator))
+        # strip off [], () or {}
+        col0Str, indicators = VariableItem.extractIndicators(self.text(0))
+        self.setText(0, "{0:6d}{1}".format(int(col0Str), indicators))
 
 
 class VariablesViewer(QTreeWidget):
@@ -254,14 +327,6 @@
         self.__debugViewer = viewer
         self.__globalScope = globalScope
         
-        self.indicators = {
-            # Python types
-            'list': '[]',
-            'tuple': '()',
-            'dict': '{}',                  # __IGNORE_WARNING__
-                           
-        }
-        
         self.rx_class = QRegExp('<.*(instance|object) at 0x.*>')
         self.rx_class2 = QRegExp('class .*')
         self.rx_class3 = QRegExp('<class .* at 0x.*>')
@@ -370,12 +435,17 @@
         else:
             count = node.childCount()
         
+        if column == 0:
+            searchStr = VariableItem.extractId(slist[0])[0]
+        else:
+            searchStr = slist[0]
+        
         for index in range(count):
             if node is None:
                 itm = self.topLevelItem(index)
             else:
                 itm = node.child(index)
-            if itm.text(column) == slist[0]:
+            if itm.text(column) == searchStr:
                 if len(slist) > 1:
                     itm = self.__findItem(slist[1:], column, itm)
                 return itm
@@ -542,12 +612,12 @@
         if parent is None:
             parent = self
         try:
-            dvar = '{0}{1}'.format(var, self.indicators[vtype])
+            dvar = '{0}{1}'.format(var, VariableItem.Type2Indicators[vtype])
         except KeyError:
             dvar = var
         dvtype = self.__getDispType(vtype)
         
-        if vtype in ['list', 'Array', 'tuple', 'dict', 'Hash']:
+        if vtype in ['list', 'tuple', 'dict', 'set', 'frozenset']:
             itm = self.__generateItem(parent, dvar,
                                       self.tr("{0} items").format(value),
                                       dvtype, True)
@@ -575,12 +645,10 @@
         @return displaystring (string)
         """
         try:
-            i = ConfigVarTypeStrings.index(vtype)
-            dvtype = self.tr(ConfigVarTypeDispStrings[i])
-        except ValueError:
+            dvtype = self.tr(ConfigVarTypeDispStrings[vtype])
+        except KeyError:
             if vtype == 'classobj':
-                dvtype = self.tr(ConfigVarTypeDispStrings[
-                    ConfigVarTypeStrings.index('instance')])
+                dvtype = self.tr(ConfigVarTypeDispStrings['instance'])
             else:
                 dvtype = vtype
         return dvtype
@@ -628,21 +696,20 @@
             return  # do not display anything, if the variable has no value
             
         vtype = itm.text(2)
-        name = itm.text(0)
-        if name[-2:] in ['[]', '{}', '()']:         # __IGNORE_WARNING__
-            name = name[:-2]
+        name = VariableItem.extractIndicators(itm.text(0).strip())[0]
         
         par = itm.parent()
         nlist = [name]
         # build up the fully qualified name
         while par is not None:
-            pname = par.text(0)
-            if pname[-2:] in ['[]', '{}', '()']:    # __IGNORE_WARNING__
+            pname, indicators = VariableItem.extractIndicators(
+                par.text(0).strip())
+            if indicators:
                 if nlist[0].endswith("."):
                     nlist[0] = '[{0}].'.format(nlist[0][:-1])
                 else:
                     nlist[0] = '[{0}]'.format(nlist[0])
-                nlist.insert(0, pname[:-2])
+                nlist.insert(0, pname)
             else:
                 nlist.insert(0, '{0}.'.format(pname))
             par = par.parent()
--- a/eric6.e4p	Wed Sep 14 20:08:16 2016 +0200
+++ b/eric6.e4p	Fri Sep 16 19:28:39 2016 +0200
@@ -36,6 +36,7 @@
     <Source>DebugClients/Python2/DebugConfig.py</Source>
     <Source>DebugClients/Python2/DebugThread.py</Source>
     <Source>DebugClients/Python2/DebugUtilities.py</Source>
+    <Source>DebugClients/Python2/DebugVariables.py</Source>
     <Source>DebugClients/Python2/FlexCompleter.py</Source>
     <Source>DebugClients/Python2/PyProfile.py</Source>
     <Source>DebugClients/Python2/__init__.py</Source>

eric ide

mercurial