eric7/DebugClients/Python/DebugVariables.py

branch
eric7
changeset 8568
890dfe038613
parent 8479
903b7d3b58af
child 8573
77845f40ebfe
diff -r 17fb004af51d -r 890dfe038613 eric7/DebugClients/Python/DebugVariables.py
--- a/eric7/DebugClients/Python/DebugVariables.py	Sun Aug 29 17:50:13 2021 +0200
+++ b/eric7/DebugClients/Python/DebugVariables.py	Sun Aug 29 19:19:31 2021 +0200
@@ -8,6 +8,9 @@
 """
 
 import contextlib
+import sys
+
+from collections.abc import ItemsView, KeysView, ValuesView
 
 from DebugConfig import ConfigQtNames, ConfigKnownQtTypes, BatchSize
 
@@ -16,7 +19,7 @@
 #
 
 ############################################################
-## Classes implementing resolvers for various compund types
+## Classes implementing resolvers for various compound types
 ############################################################
 
 
@@ -37,24 +40,20 @@
         """
         return getattr(var, attribute, None)
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @return dictionary containing the variable attributes
-        @rtype dict
+        @return list containing the variable attributes
+        @rtype list
         """
-        names = dir(var)
-        if not names and hasattr(var, "__members__"):
-            names = var.__members__
-        
-        d = {}
-        for name in names:
+        d = []
+        for name in dir(var):
             with contextlib.suppress(Exception):
                 attribute = getattr(var, name)
-                d[name] = attribute
+                d.append((name, attribute))
         
         return d
 
@@ -68,29 +67,25 @@
     """
     Class used to resolve the default way.
     """
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        names = dir(var)
-        if not names and hasattr(var, "__members__"):
-            names = var.__members__
-        
-        d = {}
-        for name in names:
+        d = []
+        for name in dir(var):
             with contextlib.suppress(Exception):
                 attribute = getattr(var, name)
-                d[name] = attribute
+                d.append((name, attribute))
         
         yield -1, d
         while True:
-            yield -2, {}
+            yield -2, []
 
 
 ############################################################
@@ -144,17 +139,17 @@
 
         return key  # __IGNORE_WARNING_M834__
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         allItems = list(var.items())
         try:
@@ -166,23 +161,23 @@
         
         for key, value in allItems:
             key = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
-            d[key] = value
+            d.append((key, value))
             count += 1
             if count >= BatchSize:
                 yield start, d
                 start += count
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        d = super().getDictionary(var)
+        d = super().getVariableList(var)
         yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
 
 
 ############################################################
@@ -210,36 +205,36 @@
         except Exception:
             return getattr(var, str(attribute), None)
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         for idx, value in enumerate(var):
-            d[idx] = value
+            d.append((idx, value))
             count += 1
             if count >= BatchSize:
                 yield start, d
                 start = idx + 1
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        d = super().getDictionary(var)
+        d = super().getVariableList(var)
         yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
     
 
 ############################################################
@@ -256,7 +251,7 @@
         Public method to get an attribute from a variable.
         
         @param var variable to extract an attribute or value from
-        @type tuple or list
+        @type dict_items, dict_keys or dict_values
         @param attribute id of the value to extract
         @type str
         @return value of the attribute
@@ -264,16 +259,17 @@
         """
         return super().resolve(list(var), attribute)
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @return dictionary containing the variable attributes
-        @rtype dict
+        @yield tuple containing the batch start index and a list
+            containing the variable attributes
+        @ytype tuple of (int, list)
         """
-        return super().getDictionary(list(var))
+        return super().getVariableList(list(var))
 
 
 ############################################################
@@ -309,36 +305,36 @@
         
         return None
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         for value in var:
             count += 1
-            d["'ID: {0}'".format(id(value))] = value
+            d.append(("'ID: {0}'".format(id(value)), value))
             if count >= BatchSize:
                 yield start, d
                 start += count
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        additionals = super().getDictionary(var)
-        yield -1, additionals
+        d = super().getVariableList(var)
+        yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
     
 
 ############################################################
@@ -369,7 +365,7 @@
         Public method to get an attribute from a variable.
         
         @param var variable to extract an attribute or value from
-        @type tuple or list
+        @type ndarray
         @param attribute id of the value to extract
         @type str
         @return value of the attribute
@@ -400,17 +396,17 @@
         
         return None
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         try:
             len(var)  # Check if it's an unsized object, e.g. np.ndarray(())
@@ -419,45 +415,45 @@
             allItems = []
         
         for idx, value in enumerate(allItems):
-            d[str(idx)] = value
+            d.append((str(idx), value))
             count += 1
             if count >= BatchSize:
                 yield start, d
                 start += count
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        d = super().getDictionary(var)
+        d = super().getVariableList(var)
         
         if var.size > 1024 * 1024:
-            d['min'] = (
-                'ndarray too big, calculating min would slow down debugging')
-            d['max'] = (
-                'ndarray too big, calculating max would slow down debugging')
-            d['mean'] = (
-                'ndarray too big, calculating mean would slow down debugging')
+            d.append(('min',
+                'ndarray too big, calculating min would slow down debugging'))
+            d.append(('max',
+                'ndarray too big, calculating max would slow down debugging'))
+            d.append(('mean',
+                'ndarray too big, calculating mean would slow down debugging'))
         elif self.__isNumeric(var):
             if var.size == 0:
-                d['min'] = 'empty array'
-                d['max'] = 'empty array'
-                d['mean'] = 'empty array'
+                d.append(('min', 'empty array'))
+                d.append(('max', 'empty array'))
+                d.append(('mean', 'empty array'))
             else:
-                d['min'] = var.min()
-                d['max'] = var.max()
-                d['mean'] = var.mean()
+                d.append(('min', var.min()))
+                d.append(('max', var.max()))
+                d.append(('mean', var.mean()))
         else:
-            d['min'] = 'not a numeric object'
-            d['max'] = 'not a numeric object'
-            d['mean'] = 'not a numeric object'
+            d.append(('min', 'not a numeric object'))
+            d.append(('max', 'not a numeric object'))
+            d.append(('mean', 'not a numeric object'))
         
         yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
 
 
 ############################################################
@@ -474,7 +470,7 @@
         Public method to get an attribute from a variable.
         
         @param var variable to extract an attribute or value from
-        @type dict
+        @type MultiValueDict
         @param attribute name of the attribute to extract
         @type str
         @return value of the attribute
@@ -493,17 +489,17 @@
         
         return None
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         allKeys = list(var.keys())
         try:
@@ -515,23 +511,23 @@
         
         for key in allKeys:
             dkey = "{0} (ID:{1})".format(self.keyToStr(key), id(key))
-            d[dkey] = var.getlist(key)
+            d.append((dkey, var.getlist(key)))
             count += 1
             if count >= BatchSize:
                 yield start, d
                 start += count
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        d = super().getDictionary(var)
+        d = super(DictResolver, self).getVariableList(var)
         yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
     
 
 ############################################################
@@ -564,7 +560,7 @@
         Public method to get an attribute from a variable.
         
         @param var variable to extract an attribute or value from
-        @type tuple or list
+        @type array.array
         @param attribute id of the value to extract
         @type str
         @return value of the attribute
@@ -577,42 +573,179 @@
         
         return None
     
-    def getDictionary(self, var):
+    def getVariableList(self, var):
         """
-        Public method to get the attributes of a variable as a dictionary.
+        Public method to get the attributes of a variable as a list.
         
         @param var variable to be converted
         @type any
-        @yield tuple containing the batch start index and a dictionary
+        @yield tuple containing the batch start index and a list
             containing the variable attributes
-        @ytype tuple of (int, dict)
+        @ytype tuple of (int, list)
         """
-        d = {}
+        d = []
         start = count = 0
         allItems = var.tolist()
         
         for idx, value in enumerate(allItems):
-            d[str(idx)] = value
+            d.append((str(idx), value))
             count += 1
             if count >= BatchSize:
                 yield start, d
                 start += count
                 count = 0
-                d = {}
+                d = []
         
         if d:
             yield start, d
         
         # in case it has additional fields
-        d = super().getDictionary(var)
+        d = super().getVariableList(var)
         
         # Special data for array type: convert typecode to readable text
-        d['type'] = self.TypeCodeMap.get(var.typecode, 'illegal type')
+        d.append(('type', self.TypeCodeMap.get(var.typecode, 'illegal type')))
         
         yield -1, d
         
         while True:
-            yield -2, {}
+            yield -2, []
+
+
+############################################################
+## PySide / PyQt Resolver
+############################################################
+
+
+class QtResolver(BaseResolver):
+    """
+    Class used to resolve the Qt implementations.
+    """
+    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 Qt objects
+        @param attribute name of the attribute to extract
+        @type str
+        @return value of the attribute
+        @rtype any
+        """
+        if attribute == 'internalPointer':
+            return var.internalPointer()
+        
+        return getattr(var, attribute, None)
+    
+    def getVariableList(self, var):
+        """
+        Public method to get the attributes of a variable as a list.
+        
+        @param var variable to be converted
+        @type any
+        @yield tuple containing the batch start index and a list
+            containing the variable attributes
+        @ytype tuple of (int, list)
+        """
+        d = []
+        attributes = ()
+        # Gently handle exception which could occure as special
+        # cases, e.g. already deleted C++ objects, str conversion..
+        try:
+            qttype = type(var).__name__
+            
+            if qttype in ('QLabel', 'QPushButton'):
+                attributes = ('text', )
+            elif qttype == 'QByteArray':
+                d.append(('bytes', bytes(var)))
+                d.append(('hex', "QByteArray", "{0}".format(var.toHex())))
+                d.append(('base64', "QByteArray",
+                    "{0}".format(var.toBase64())))
+                d.append(('percent encoding', "QByteArray",
+                    "{0}".format(var.toPercentEncoding())))
+            elif qttype in ('QPoint', 'QPointF'):
+                attributes = ('x', 'y')
+            elif qttype in ('QRect', 'QRectF'):
+                attributes = ('x', 'y', 'width', 'height')
+            elif qttype in ('QSize', 'QSizeF'):
+                attributes = ('width', 'height')
+            elif qttype == 'QColor':
+                attributes = ('name', )
+                r, g, b, a = var.getRgb()
+                d.append(
+                    ('rgba', "{0:d}, {1:d}, {2:d}, {3:d}".format(r, g, b, a))
+                )
+                h, s, v, a = var.getHsv()
+                d.append(
+                    ('hsva', "{0:d}, {1:d}, {2:d}, {3:d}".format(h, s, v, a))
+                )
+                c, m, y, k, a = var.getCmyk()
+                d.append(
+                    ('cmyka',
+                    "{0:d}, {1:d}, {2:d}, {3:d}, {4:d}".format(c, m, y, k, a))
+                )
+            elif qttype in ('QDate', 'QTime', 'QDateTime'):
+                d.append((qttype[1:].lower(), var.toString()))
+            elif qttype == 'QDir':
+                attributes = ('path', 'absolutePath', 'canonicalPath')
+            elif qttype == 'QFile':
+                attributes = ('fileName', )
+            elif qttype == 'QFont':
+                attributes = (
+                    'family', 'pointSize', 'weight', 'bold', 'italic'
+                )
+            elif qttype == 'QUrl':
+                d.append(('url', var.toString()))
+                attributes = ('scheme', 'userName', 'password', 'host', 'port',
+                              'path')
+            elif qttype == 'QModelIndex':
+                valid = var.isValid()
+                d.append(('valid', valid))
+                if valid:
+                    d.append(("internalPointer", var.internalPointer()))
+                    attributes = ('row', 'column', 'internalId')
+            elif qttype in ('QRegExp', "QRegularExpression"):
+                attributes = ('pattern', )
+                
+            # GUI stuff
+            elif qttype == 'QAction':
+                d.append(('shortcut', var.shortcut().toString()))
+                attributes = ('objectName', 'text', 'iconText', 'toolTip',
+                              'whatsThis')
+                
+            elif qttype == 'QKeySequence':
+                d.append(('keySequence', var.toString()))
+                
+            # XML stuff
+            elif qttype == 'QDomAttr':
+                attributes = ('name', 'var')
+            elif qttype in ('QDomCharacterData', 'QDomComment', 'QDomText'):
+                attributes = ('data', )
+            elif qttype == 'QDomDocument':
+                d.append(('text', var.toString()))
+            elif qttype == 'QDomElement':
+                attributes = ('tagName', 'text')
+                
+            # Networking stuff
+            elif qttype == 'QHostAddress':
+                d.append(('address', var.toString()))
+                
+            # PySide specific
+            elif qttype == 'EnumType':  # Not in PyQt possible
+                for key, value in var.values.items():
+                    d.append((key, int(value)))
+        except Exception:
+            pass
+        
+        for attribute in attributes:
+            d.append((attribute, getattr(var, attribute)()))
+        
+        # add additional fields
+        if qttype != 'EnumType':
+            d.extend(super().getVariableList(var))
+        
+        yield -1, d
+        while True:
+            yield -2, []
 
 
 defaultResolver = DefaultResolver()
@@ -623,13 +756,17 @@
 ndarrayResolver = NdArrayResolver()
 multiValueDictResolver = MultiValueDictResolver()
 arrayResolver = ArrayResolver()
+qtResolver = QtResolver()
+
 
 ############################################################
 ## Methods to determine the type of a variable and the
 ## resolver class to use
 ############################################################
 
-_TypeMap = None
+_TypeMap = _ArrayTypes = None
+_TryArray = _TryNumpy = _TryDjango = True
+_MapCount = 0
 
 
 def _initTypeMap():
@@ -638,77 +775,79 @@
     """
     global _TypeMap
     
+    # Type map for special handling of array types.
+    # All other types not listed here use the default resolver.
     _TypeMap = [
-        (type(None), None,),
-        (int, None),
-        (float, None),
-        (complex, None),
-        (str, None),
         (tuple, listResolver),
         (list, listResolver),
         (dict, dictResolver),
         (set, setResolver),
         (frozenset, setResolver),
+        (ItemsView, dictViewResolver),  # Since Python 3.0
+        (KeysView, dictViewResolver),
+        (ValuesView, dictViewResolver),
     ]
+
+
+# Initialize the static type map
+_initTypeMap()
+
+
+def updateTypeMap():
+    """
+    Public function to update the type map based on module imports.
+    """
+    global _TypeMap, _ArrayTypes, _TryArray, _TryNumpy, _TryDjango, _MapCount
     
-    with contextlib.suppress(Exception):
-        _TypeMap.append((long, None))           # __IGNORE_WARNING__
-
-    with contextlib.suppress(ImportError):
+    # array.array may not be imported (yet)
+    if _TryArray and 'array' in sys.modules:
         import array
         _TypeMap.append((array.array, arrayResolver))
-        # array.array may not be available
+        _TryArray = False
     
-    with contextlib.suppress(ImportError):
+    # numpy may not be imported (yet)
+    if _TryNumpy and 'numpy' in sys.modules:
         import numpy
         _TypeMap.append((numpy.ndarray, ndarrayResolver))
-        # numpy may not be installed
+        _TryNumpy = False
     
-    with contextlib.suppress(ImportError):
+    # django may not be imported (yet)
+    if _TryDjango and 'django' in sys.modules:
         from django.utils.datastructures import MultiValueDict
         # it should go before dict
         _TypeMap.insert(0, (MultiValueDict, multiValueDictResolver))
-        # django may not be installed
+        _TryDjango = False
     
-    with contextlib.suppress(ImportError):
-        from collections.abc import ItemsView, KeysView, ValuesView
-        _TypeMap.append((ItemsView, dictViewResolver))
-        _TypeMap.append((KeysView, dictViewResolver))
-        _TypeMap.append((ValuesView, dictViewResolver))
-        # not available on all Python versions
+    # If _TypeMap changed, rebuild the _ArrayTypes tuple
+    if _MapCount != len(_TypeMap):
+        _ArrayTypes = tuple(typ for typ, _resolver in _TypeMap)
+        _MapCount = len(_TypeMap)
 
 
-def getType(obj):
+def getResolver(obj):
     """
-    Public method to get the type information for an object.
+    Public method to get the resolver based on the type info of an object.
     
-    @param obj object to get type information for
+    @param obj object to get resolver for
     @type any
-    @return tuple containing the type name, type string and resolver
-    @rtype tuple of str, str, BaseResolver
+    @return resolver
+    @rtype BaseResolver
     """
-    typeObject = type(obj)
-    typeName = typeObject.__name__
     # Between PyQt and PySide the returned type is different (class vs. type)
-    typeStr = str(typeObject).split(' ', 1)[-1]
+    typeStr = str(type(obj)).split(' ', 1)[-1]
     typeStr = typeStr[1:-2]
     
     if (
         typeStr.startswith(ConfigQtNames) and
         typeStr.endswith(ConfigKnownQtTypes)
     ):
-        resolver = None
-    else:
-        if _TypeMap is None:
-            _initTypeMap()
-        
-        for typeData, resolver in _TypeMap:  # __IGNORE_WARNING_M507__
-            if isinstance(obj, typeData):
-                break
-        else:
-            resolver = defaultResolver
+        return qtResolver
     
-    return typeName, typeStr, resolver
+    for typeData, resolver in _TypeMap:  # __IGNORE_WARNING_M507__
+        if isinstance(obj, typeData):
+            return resolver
+    
+    return defaultResolver
 
 #
 # eflag: noqa = Y113

eric ide

mercurial