Started refactoring the code style checker.

Mon, 12 Oct 2020 19:54:03 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 12 Oct 2020 19:54:03 +0200
changeset 7782
976d3b19ba7f
parent 7781
607a6098cb44
child 7783
36f66ce496bd

Started refactoring the code style checker.

eric6.e4p file | annotate | diff | comparison | revisions
eric6/Documentation/Source/eric6.Plugins.CheckerPlugins.CodeStyleChecker.AnnotationsChecker.html file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/__init__.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/translations.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/AnnotationsChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/__init__.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/translations.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/ComplexityChecker.py file | annotate | diff | comparison | revisions
eric6/Plugins/CheckerPlugins/CodeStyleChecker/translations.py file | annotate | diff | comparison | revisions
--- a/eric6.e4p	Mon Oct 12 19:28:42 2020 +0200
+++ b/eric6.e4p	Mon Oct 12 19:54:03 2020 +0200
@@ -307,7 +307,9 @@
     <Source>eric6/PluginManager/__init__.py</Source>
     <Source>eric6/Plugins/AboutPlugin/AboutDialog.py</Source>
     <Source>eric6/Plugins/AboutPlugin/__init__.py</Source>
-    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/AnnotationsChecker.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsChecker.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/__init__.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/translations.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/AstUtilities.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleAddBuiltinIgnoreDialog.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py</Source>
@@ -315,7 +317,9 @@
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCodeSelectionDialog.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleFixer.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleStatisticsDialog.py</Source>
-    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/ComplexityChecker.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/__init__.py</Source>
+    <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/translations.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/DocStyleChecker.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py</Source>
     <Source>eric6/Plugins/CheckerPlugins/CodeStyleChecker/NamingStyleChecker.py</Source>
--- a/eric6/Documentation/Source/eric6.Plugins.CheckerPlugins.CodeStyleChecker.AnnotationsChecker.html	Mon Oct 12 19:28:42 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,516 +0,0 @@
-<!DOCTYPE html>
-<html><head>
-<title>eric6.Plugins.CheckerPlugins.CodeStyleChecker.AnnotationsChecker</title>
-<meta charset="UTF-8">
-<style>
-body {
-    background: #EDECE6;
-    margin: 0em 1em 10em 1em;
-    color: black;
-}
-
-h1 { color: white; background: #85774A; }
-h2 { color: white; background: #85774A; }
-h3 { color: white; background: #9D936E; }
-h4 { color: white; background: #9D936E; }
-    
-a { color: #BA6D36; }
-
-</style>
-</head>
-<body>
-<a NAME="top" ID="top"></a>
-<h1>eric6.Plugins.CheckerPlugins.CodeStyleChecker.AnnotationsChecker</h1>
-
-<p>
-Module implementing a checker for function type annotations.
-</p>
-<h3>Global Attributes</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Classes</h3>
-
-<table>
-
-<tr>
-<td><a href="#AnnotationsChecker">AnnotationsChecker</a></td>
-<td>Class implementing a checker for function type annotations.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor">FunctionVisitor</a></td>
-<td>Class implementing a node visitor to check function annotations.</td>
-</tr>
-</table>
-<h3>Functions</h3>
-
-<table>
-
-<tr>
-<td><a href="#getAnnotationComplexity">getAnnotationComplexity</a></td>
-<td>Function to determine the annotation complexity.</td>
-</tr>
-<tr>
-<td><a href="#hasTypeAnnotations">hasTypeAnnotations</a></td>
-<td>Function to check for type annotations.</td>
-</tr>
-</table>
-<hr />
-<hr />
-<a NAME="AnnotationsChecker" ID="AnnotationsChecker"></a>
-<h2>AnnotationsChecker</h2>
-
-<p>
-    Class implementing a checker for function type annotations.
-</p>
-<h3>Derived from</h3>
-object
-<h3>Class Attributes</h3>
-
-<table>
-<tr><td>Codes</td></tr>
-</table>
-<h3>Class Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Methods</h3>
-
-<table>
-
-<tr>
-<td><a href="#AnnotationsChecker.__init__">AnnotationsChecker</a></td>
-<td>Constructor</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__checkAnnotationComplexity">__checkAnnotationComplexity</a></td>
-<td>Private method to check the type annotation complexity.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__checkAnnotationsCoverage">__checkAnnotationsCoverage</a></td>
-<td>Private method to check for function annotation coverage.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__checkFunctionAnnotations">__checkFunctionAnnotations</a></td>
-<td>Private method to check for function annotation issues.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__error">__error</a></td>
-<td>Private method to record an issue.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__generateTree">__generateTree</a></td>
-<td>Private method to generate an AST for our source.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__ignoreCode">__ignoreCode</a></td>
-<td>Private method to check if the message code should be ignored.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.__reportInvalidSyntax">__reportInvalidSyntax</a></td>
-<td>Private method to report a syntax error.</td>
-</tr>
-<tr>
-<td><a href="#AnnotationsChecker.run">run</a></td>
-<td>Public method to check the given source against annotation issues.</td>
-</tr>
-</table>
-<h3>Static Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-
-<a NAME="AnnotationsChecker.__init__" ID="AnnotationsChecker.__init__"></a>
-<h4>AnnotationsChecker (Constructor)</h4>
-<b>AnnotationsChecker</b>(<i>source, filename, select, ignore, expected, repeat, args</i>)
-
-<p>
-        Constructor
-</p>
-<dl>
-
-<dt><i>source</i> (list of str)</dt>
-<dd>
-source code to be checked
-</dd>
-<dt><i>filename</i> (str)</dt>
-<dd>
-name of the source file
-</dd>
-<dt><i>select</i> (list of str)</dt>
-<dd>
-list of selected codes
-</dd>
-<dt><i>ignore</i> (list of str)</dt>
-<dd>
-list of codes to be ignored
-</dd>
-<dt><i>expected</i> (list of str)</dt>
-<dd>
-list of expected codes
-</dd>
-<dt><i>repeat</i> (bool)</dt>
-<dd>
-flag indicating to report each occurrence of a code
-</dd>
-<dt><i>args</i> (dict)</dt>
-<dd>
-dictionary of arguments for the annotation checks
-</dd>
-</dl>
-<a NAME="AnnotationsChecker.__checkAnnotationComplexity" ID="AnnotationsChecker.__checkAnnotationComplexity"></a>
-<h4>AnnotationsChecker.__checkAnnotationComplexity</h4>
-<b>__checkAnnotationComplexity</b>(<i></i>)
-
-<p>
-        Private method to check the type annotation complexity.
-</p>
-<a NAME="AnnotationsChecker.__checkAnnotationsCoverage" ID="AnnotationsChecker.__checkAnnotationsCoverage"></a>
-<h4>AnnotationsChecker.__checkAnnotationsCoverage</h4>
-<b>__checkAnnotationsCoverage</b>(<i></i>)
-
-<p>
-        Private method to check for function annotation coverage.
-</p>
-<a NAME="AnnotationsChecker.__checkFunctionAnnotations" ID="AnnotationsChecker.__checkFunctionAnnotations"></a>
-<h4>AnnotationsChecker.__checkFunctionAnnotations</h4>
-<b>__checkFunctionAnnotations</b>(<i></i>)
-
-<p>
-        Private method to check for function annotation issues.
-</p>
-<a NAME="AnnotationsChecker.__error" ID="AnnotationsChecker.__error"></a>
-<h4>AnnotationsChecker.__error</h4>
-<b>__error</b>(<i>lineNumber, offset, code, *args</i>)
-
-<p>
-        Private method to record an issue.
-</p>
-<dl>
-
-<dt><i>lineNumber</i> (int)</dt>
-<dd>
-line number of the issue
-</dd>
-<dt><i>offset</i> (int)</dt>
-<dd>
-position within line of the issue
-</dd>
-<dt><i>code</i> (str)</dt>
-<dd>
-message code
-</dd>
-<dt><i>args</i> (list)</dt>
-<dd>
-arguments for the message
-</dd>
-</dl>
-<a NAME="AnnotationsChecker.__generateTree" ID="AnnotationsChecker.__generateTree"></a>
-<h4>AnnotationsChecker.__generateTree</h4>
-<b>__generateTree</b>(<i></i>)
-
-<p>
-        Private method to generate an AST for our source.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-generated AST
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-ast.Module
-</dd>
-</dl>
-<a NAME="AnnotationsChecker.__ignoreCode" ID="AnnotationsChecker.__ignoreCode"></a>
-<h4>AnnotationsChecker.__ignoreCode</h4>
-<b>__ignoreCode</b>(<i>code</i>)
-
-<p>
-        Private method to check if the message code should be ignored.
-</p>
-<dl>
-
-<dt><i>code</i> (str)</dt>
-<dd>
-message code to check for
-</dd>
-</dl>
-<dl>
-<dt>Returns:</dt>
-<dd>
-flag indicating to ignore the given code
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-bool
-</dd>
-</dl>
-<a NAME="AnnotationsChecker.__reportInvalidSyntax" ID="AnnotationsChecker.__reportInvalidSyntax"></a>
-<h4>AnnotationsChecker.__reportInvalidSyntax</h4>
-<b>__reportInvalidSyntax</b>(<i></i>)
-
-<p>
-        Private method to report a syntax error.
-</p>
-<a NAME="AnnotationsChecker.run" ID="AnnotationsChecker.run"></a>
-<h4>AnnotationsChecker.run</h4>
-<b>run</b>(<i></i>)
-
-<p>
-        Public method to check the given source against annotation issues.
-</p>
-<div align="right"><a href="#top">Up</a></div>
-<hr />
-<hr />
-<a NAME="FunctionVisitor" ID="FunctionVisitor"></a>
-<h2>FunctionVisitor</h2>
-
-<p>
-    Class implementing a node visitor to check function annotations.
-</p>
-<p>
-    Note: this class is modelled after flake8-annotations checker.
-</p>
-<h3>Derived from</h3>
-ast.NodeVisitor
-<h3>Class Attributes</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Class Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Methods</h3>
-
-<table>
-
-<tr>
-<td><a href="#FunctionVisitor.__init__">FunctionVisitor</a></td>
-<td>Constructor</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.__checkFunctionNode">__checkFunctionNode</a></td>
-<td>Private method to check an individual function definition node.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.__classifyArgumentError">__classifyArgumentError</a></td>
-<td>Private method to classify and record an argument annotation issue.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.__classifyReturnError">__classifyReturnError</a></td>
-<td>Private method to classify and record a return annotation issue.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.visit_AsyncFunctionDef">visit_AsyncFunctionDef</a></td>
-<td>Public method to handle an async function or method definition.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.visit_ClassDef">visit_ClassDef</a></td>
-<td>Public method to handle class definitions.</td>
-</tr>
-<tr>
-<td><a href="#FunctionVisitor.visit_FunctionDef">visit_FunctionDef</a></td>
-<td>Public method to handle a function or method definition.</td>
-</tr>
-</table>
-<h3>Static Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-
-<a NAME="FunctionVisitor.__init__" ID="FunctionVisitor.__init__"></a>
-<h4>FunctionVisitor (Constructor)</h4>
-<b>FunctionVisitor</b>(<i>sourceLines</i>)
-
-<p>
-        Constructor
-</p>
-<dl>
-
-<dt><i>sourceLines</i> (list of str)</dt>
-<dd>
-lines of source code
-</dd>
-</dl>
-<a NAME="FunctionVisitor.__checkFunctionNode" ID="FunctionVisitor.__checkFunctionNode"></a>
-<h4>FunctionVisitor.__checkFunctionNode</h4>
-<b>__checkFunctionNode</b>(<i>node, classMethod=False</i>)
-
-<p>
-        Private method to check an individual function definition node.
-</p>
-<dl>
-
-<dt><i>node</i> (ast.FunctionDef or ast.AsyncFunctionDef)</dt>
-<dd>
-reference to the node to be processed
-</dd>
-<dt><i>classMethod</i> (bool)</dt>
-<dd>
-flag indicating a class method
-</dd>
-</dl>
-<a NAME="FunctionVisitor.__classifyArgumentError" ID="FunctionVisitor.__classifyArgumentError"></a>
-<h4>FunctionVisitor.__classifyArgumentError</h4>
-<b>__classifyArgumentError</b>(<i>argNode, argType, methodType</i>)
-
-<p>
-        Private method to classify and record an argument annotation issue.
-</p>
-<dl>
-
-<dt><i>argNode</i> (ast.arguments)</dt>
-<dd>
-reference to the argument node
-</dd>
-<dt><i>argType</i> (str)</dt>
-<dd>
-type of the argument node
-</dd>
-<dt><i>methodType</i> (str)</dt>
-<dd>
-type of method/function the argument belongs to
-</dd>
-</dl>
-<a NAME="FunctionVisitor.__classifyReturnError" ID="FunctionVisitor.__classifyReturnError"></a>
-<h4>FunctionVisitor.__classifyReturnError</h4>
-<b>__classifyReturnError</b>(<i>methodType, visibilityType, lineno, colOffset</i>)
-
-<p>
-        Private method to classify and record a return annotation issue.
-</p>
-<dl>
-
-<dt><i>methodType</i> (str)</dt>
-<dd>
-type of method/function the argument belongs to
-</dd>
-<dt><i>visibilityType</i> (str)</dt>
-<dd>
-visibility of the function
-</dd>
-<dt><i>lineno</i> (int)</dt>
-<dd>
-line number
-</dd>
-<dt><i>colOffset</i> (int)</dt>
-<dd>
-column number
-</dd>
-</dl>
-<a NAME="FunctionVisitor.visit_AsyncFunctionDef" ID="FunctionVisitor.visit_AsyncFunctionDef"></a>
-<h4>FunctionVisitor.visit_AsyncFunctionDef</h4>
-<b>visit_AsyncFunctionDef</b>(<i>node</i>)
-
-<p>
-        Public method to handle an async function or method definition.
-</p>
-<dl>
-
-<dt><i>node</i> (ast.AsyncFunctionDef)</dt>
-<dd>
-reference to the node to be processed
-</dd>
-</dl>
-<a NAME="FunctionVisitor.visit_ClassDef" ID="FunctionVisitor.visit_ClassDef"></a>
-<h4>FunctionVisitor.visit_ClassDef</h4>
-<b>visit_ClassDef</b>(<i>node</i>)
-
-<p>
-        Public method to handle class definitions.
-</p>
-<dl>
-
-<dt><i>node</i> (ast.ClassDef)</dt>
-<dd>
-reference to the node to be processed
-</dd>
-</dl>
-<a NAME="FunctionVisitor.visit_FunctionDef" ID="FunctionVisitor.visit_FunctionDef"></a>
-<h4>FunctionVisitor.visit_FunctionDef</h4>
-<b>visit_FunctionDef</b>(<i>node</i>)
-
-<p>
-        Public method to handle a function or method definition.
-</p>
-<dl>
-
-<dt><i>node</i> (ast.FunctionDef)</dt>
-<dd>
-reference to the node to be processed
-</dd>
-</dl>
-<div align="right"><a href="#top">Up</a></div>
-<hr />
-<hr />
-<a NAME="getAnnotationComplexity" ID="getAnnotationComplexity"></a>
-<h2>getAnnotationComplexity</h2>
-<b>getAnnotationComplexity</b>(<i>annotationNode</i>)
-
-<p>
-    Function to determine the annotation complexity.
-</p>
-<dl>
-
-<dt><i>annotationNode</i> (ast.AST)</dt>
-<dd>
-reference to the node to determine the annotation
-        complexity for
-</dd>
-</dl>
-<dl>
-<dt>Returns:</dt>
-<dd>
-annotation complexity
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-= int
-</dd>
-</dl>
-<div align="right"><a href="#top">Up</a></div>
-<hr />
-<hr />
-<a NAME="hasTypeAnnotations" ID="hasTypeAnnotations"></a>
-<h2>hasTypeAnnotations</h2>
-<b>hasTypeAnnotations</b>(<i>funcNode</i>)
-
-<p>
-    Function to check for type annotations.
-</p>
-<dl>
-
-<dt><i>funcNode</i> (ast.AsyncFunctionDef or ast.FunctionDef)</dt>
-<dd>
-reference to the function definition node to be checked
-</dd>
-</dl>
-<dl>
-<dt>Returns:</dt>
-<dd>
-flag indicating the presence of type annotations
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-bool
-</dd>
-</dl>
-<div align="right"><a href="#top">Up</a></div>
-<hr />
-</body></html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/AnnotationsChecker.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,465 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a checker for function type annotations.
+"""
+
+import sys
+import ast
+
+import AstUtilities
+
+
+class AnnotationsChecker(object):
+    """
+    Class implementing a checker for function type annotations.
+    """
+    Codes = [
+        ## Function Annotations
+        "A001", "A002", "A003",
+        
+        ## Method Annotations
+        "A101", "A102",
+        
+        ## Return Annotations
+        "A201", "A202", "A203", "A204", "A205", "A206",
+        
+        ## Annotation Coverage
+        "A881",
+        
+        ## Annotation Complexity
+        "A891",
+        
+        ## Syntax Error
+        "A999",
+    ]
+
+    def __init__(self, source, filename, select, ignore, expected, repeat,
+                 args):
+        """
+        Constructor
+        
+        @param source source code to be checked
+        @type list of str
+        @param filename name of the source file
+        @type str
+        @param select list of selected codes
+        @type list of str
+        @param ignore list of codes to be ignored
+        @type list of str
+        @param expected list of expected codes
+        @type list of str
+        @param repeat flag indicating to report each occurrence of a code
+        @type bool
+        @param args dictionary of arguments for the annotation checks
+        @type dict
+        """
+        self.__select = tuple(select)
+        self.__ignore = ('',) if select else tuple(ignore)
+        self.__expected = expected[:]
+        self.__repeat = repeat
+        self.__filename = filename
+        self.__source = source[:]
+        self.__args = args
+
+        # statistics counters
+        self.counters = {}
+        
+        # collection of detected errors
+        self.errors = []
+        
+        checkersWithCodes = [
+            (
+                self.__checkFunctionAnnotations,
+                ("A001", "A002", "A003", "A101", "A102",
+                 "A201", "A202", "A203", "A204", "A205", "A206",)
+            ),
+            (self.__checkAnnotationsCoverage, ("A881",)),
+            (self.__checkAnnotationComplexity, ("A891",)),
+        ]
+        
+        self.__defaultArgs = {
+            "MinimumCoverage": 75,      # % of type annotation coverage
+            "MaximumComplexity": 3,
+        }
+        
+        self.__checkers = []
+        for checker, codes in checkersWithCodes:
+            if any(not (code and self.__ignoreCode(code))
+                    for code in codes):
+                self.__checkers.append(checker)
+    
+    def __ignoreCode(self, code):
+        """
+        Private method to check if the message code should be ignored.
+
+        @param code message code to check for
+        @type str
+        @return flag indicating to ignore the given code
+        @rtype bool
+        """
+        return (code.startswith(self.__ignore) and
+                not code.startswith(self.__select))
+    
+    def __error(self, lineNumber, offset, code, *args):
+        """
+        Private method to record an issue.
+        
+        @param lineNumber line number of the issue
+        @type int
+        @param offset position within line of the issue
+        @type int
+        @param code message code
+        @type str
+        @param args arguments for the message
+        @type list
+        """
+        if self.__ignoreCode(code):
+            return
+        
+        if code in self.counters:
+            self.counters[code] += 1
+        else:
+            self.counters[code] = 1
+        
+        # Don't care about expected codes
+        if code in self.__expected:
+            return
+        
+        if code and (self.counters[code] == 1 or self.__repeat):
+            # record the issue with one based line number
+            self.errors.append(
+                {
+                    "file": self.__filename,
+                    "line": lineNumber + 1,
+                    "offset": offset,
+                    "code": code,
+                    "args": args,
+                }
+            )
+    
+    def __reportInvalidSyntax(self):
+        """
+        Private method to report a syntax error.
+        """
+        exc_type, exc = sys.exc_info()[:2]
+        if len(exc.args) > 1:
+            offset = exc.args[1]
+            if len(offset) > 2:
+                offset = offset[1:3]
+        else:
+            offset = (1, 0)
+        self.__error(offset[0] - 1, offset[1] or 0,
+                     'A999', exc_type.__name__, exc.args[0])
+    
+    def __generateTree(self):
+        """
+        Private method to generate an AST for our source.
+        
+        @return generated AST
+        @rtype ast.Module
+        """
+        source = "".join(self.__source)
+        return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST)
+    
+    def run(self):
+        """
+        Public method to check the given source against annotation issues.
+        """
+        if not self.__filename:
+            # don't do anything, if essential data is missing
+            return
+        
+        if not self.__checkers:
+            # don't do anything, if no codes were selected
+            return
+        
+        try:
+            self.__tree = self.__generateTree()
+        except (SyntaxError, TypeError):
+            self.__reportInvalidSyntax()
+            return
+        
+        for check in self.__checkers:
+            check()
+    
+    def __checkFunctionAnnotations(self):
+        """
+        Private method to check for function annotation issues.
+        """
+        visitor = FunctionVisitor(self.__source)
+        visitor.visit(self.__tree)
+        for issue in visitor.issues:
+            node = issue[0]
+            reason = issue[1]
+            params = issue[2:]
+            self.__error(node.lineno - 1, node.col_offset, reason, *params)
+    
+    def __checkAnnotationsCoverage(self):
+        """
+        Private method to check for function annotation coverage.
+        """
+        minAnnotationsCoverage = self.__args.get(
+            "MinimumCoverage", self.__defaultArgs["MinimumCoverage"])
+        if minAnnotationsCoverage == 0:
+            # 0 means it is switched off
+            return
+        
+        functionDefs = [
+            f for f in ast.walk(self.__tree)
+            if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef))
+        ]
+        if not functionDefs:
+            # no functions/methods at all
+            return
+        
+        functionDefAnnotationsInfo = [
+            hasTypeAnnotations(f) for f in functionDefs
+        ]
+        annotationsCoverage = int(
+            len(list(filter(None, functionDefAnnotationsInfo))) /
+            len(functionDefAnnotationsInfo) * 100
+        )
+        if annotationsCoverage < minAnnotationsCoverage:
+            self.__error(0, 0, "A881", annotationsCoverage)
+    
+    def __checkAnnotationComplexity(self):
+        """
+        Private method to check the type annotation complexity.
+        """
+        maxAnnotationComplexity = self.__args.get(
+            "MaximumComplexity", self.__defaultArgs["MaximumComplexity"])
+        typeAnnotations = []
+        
+        functionDefs = [
+            f for f in ast.walk(self.__tree)
+            if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef))
+        ]
+        for functionDef in functionDefs:
+            typeAnnotations += list(filter(
+                None, [a.annotation for a in functionDef.args.args]))
+            if functionDef.returns:
+                typeAnnotations.append(functionDef.returns)
+        typeAnnotations += [a.annotation for a in ast.walk(self.__tree)
+                            if isinstance(a, ast.AnnAssign) and a.annotation]
+        for annotation in typeAnnotations:
+            complexity = getAnnotationComplexity(annotation)
+            if complexity > maxAnnotationComplexity:
+                self.__error(annotation.lineno - 1, annotation.col_offset,
+                             "A891", complexity, maxAnnotationComplexity)
+
+
+class FunctionVisitor(ast.NodeVisitor):
+    """
+    Class implementing a node visitor to check function annotations.
+    
+    Note: this class is modelled after flake8-annotations checker.
+    """
+    def __init__(self, sourceLines):
+        """
+        Constructor
+        
+        @param sourceLines lines of source code
+        @type list of str
+        """
+        super(FunctionVisitor, self).__init__()
+        
+        self.__sourceLines = sourceLines
+        
+        self.issues = []
+    
+    def visit_FunctionDef(self, node):
+        """
+        Public method to handle a function or method definition.
+        
+        @param node reference to the node to be processed
+        @type ast.FunctionDef
+        """
+        self.__checkFunctionNode(node)
+        self.generic_visit(node)
+    
+    def visit_AsyncFunctionDef(self, node):
+        """
+        Public method to handle an async function or method definition.
+        
+        @param node reference to the node to be processed
+        @type ast.AsyncFunctionDef
+        """
+        self.__checkFunctionNode(node)
+        self.generic_visit(node)
+    
+    def visit_ClassDef(self, node):
+        """
+        Public method to handle class definitions.
+        
+        @param node reference to the node to be processed
+        @type ast.ClassDef
+        """
+        methodNodes = [
+            childNode for childNode in node.body
+            if isinstance(childNode, (ast.FunctionDef, ast.AsyncFunctionDef))
+        ]
+        for methodNode in methodNodes:
+            self.__checkFunctionNode(methodNode, classMethod=True)
+    
+    def __checkFunctionNode(self, node, classMethod=False):
+        """
+        Private method to check an individual function definition node.
+        
+        @param node reference to the node to be processed
+        @type ast.FunctionDef or ast.AsyncFunctionDef
+        @param classMethod flag indicating a class method
+        @type bool
+        """
+        if node.name.startswith("__") and node.name.endswith("__"):
+            visibilityType = "special"
+        elif node.name.startswith("__"):
+            visibilityType = "private"
+        elif node.name.startswith("_"):
+            visibilityType = "protected"
+        else:
+            visibilityType = "public"
+        
+        if classMethod:
+            decorators = [
+                decorator.id for decorator in node.decorator_list
+                if isinstance(decorator, ast.Name)
+            ]
+            if "classmethod" in decorators:
+                classMethodType = "decorator"
+            elif "staticmethod" in decorators:
+                classMethodType = "staticmethod"
+            else:
+                classMethodType = ""
+        else:
+            classMethodType = "function"
+        
+        # check argument annotations
+        for argType in ("args", "vararg", "kwonlyargs", "kwarg"):
+            args = node.args.__getattribute__(argType)
+            if args:
+                if not isinstance(args, list):
+                    args = [args]
+                
+                for arg in args:
+                    if not arg.annotation:
+                        self.__classifyArgumentError(
+                            arg, argType, classMethodType)
+        
+        # check function return annotation
+        if not node.returns:
+            lineno = node.lineno
+            colOffset = self.__sourceLines[lineno - 1].rfind(":") + 1
+            self.__classifyReturnError(classMethodType, visibilityType,
+                                       lineno, colOffset)
+    
+    def __classifyReturnError(self, methodType, visibilityType, lineno,
+                              colOffset):
+        """
+        Private method to classify and record a return annotation issue.
+        
+        @param methodType type of method/function the argument belongs to
+        @type str
+        @param visibilityType visibility of the function
+        @type str
+        @param lineno line number
+        @type int
+        @param colOffset column number
+        @type int
+        """
+        # create a dummy AST node to report line and column
+        node = ast.AST()
+        node.lineno = lineno
+        node.col_offset = colOffset
+        
+        # now classify the issue
+        if methodType == "classmethod":
+            self.issues.append((node, "A206"))
+        elif methodType == "staticmethod":
+            self.issues.append((node, "A205"))
+        elif visibilityType == "special":
+            self.issues.append((node, "A204"))
+        elif visibilityType == "private":
+            self.issues.append((node, "A203"))
+        elif visibilityType == "protected":
+            self.issues.append((node, "A202"))
+        else:
+            self.issues.append((node, "A201"))
+    
+    def __classifyArgumentError(self, argNode, argType, methodType):
+        """
+        Private method to classify and record an argument annotation issue.
+        
+        @param argNode reference to the argument node
+        @type ast.arguments
+        @param argType type of the argument node
+        @type str
+        @param methodType type of method/function the argument belongs to
+        @type str
+        """
+        # check class method issues
+        if methodType != "function":
+            if argNode.arg in ("cls", "self"):
+                if methodType == "classmethod":
+                    self.issues.append((argNode, "A102"))
+                    return
+                elif methodType != "staticmethod":
+                    self.issues.append((argNode, "A101"))
+                    return
+        
+        # check all other arguments
+        if argType == "kwarg":
+            self.issues.append((argNode, "A003", argNode.arg))
+        elif argType == "vararg":
+            self.issues.append((argNode, "A002", argNode.arg))
+        else:
+            # args and kwonlyargs
+            self.issues.append((argNode, "A001", argNode.arg))
+
+######################################################################
+## some utility functions below
+######################################################################
+
+
+def hasTypeAnnotations(funcNode):
+    """
+    Function to check for type annotations.
+    
+    @param funcNode reference to the function definition node to be checked
+    @type ast.AsyncFunctionDef or ast.FunctionDef
+    @return flag indicating the presence of type annotations
+    @rtype bool
+    """
+    hasReturnAnnotation = funcNode.returns is not None
+    hasArgsAnnotations = any(a for a in funcNode.args.args
+                             if a.annotation is not None)
+    hasKwargsAnnotations = (funcNode.args and
+                            funcNode.args.kwarg and
+                            funcNode.args.kwarg.annotation is not None)
+    hasKwonlyargsAnnotations = any(a for a in funcNode.args.kwonlyargs
+                                   if a.annotation is not None)
+    
+    return any((hasReturnAnnotation, hasArgsAnnotations, hasKwargsAnnotations,
+               hasKwonlyargsAnnotations))
+
+
+def getAnnotationComplexity(annotationNode):
+    """
+    Function to determine the annotation complexity.
+    
+    @param annotationNode reference to the node to determine the annotation
+        complexity for
+    @type ast.AST
+    @return annotation complexity
+    @rtype = int
+    """
+    if AstUtilities.isString(annotationNode):
+        annotationNode = ast.parse(annotationNode.s).body[0].value
+    if isinstance(annotationNode, ast.Subscript):
+        return 1 + getAnnotationComplexity(annotationNode.slice.value)
+    if isinstance(annotationNode, ast.Tuple):
+        return max(getAnnotationComplexity(n) for n in annotationNode.elts)
+    return 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/__init__.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the function type annotations checker.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Annotations/translations.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing message translations for the code style plugin messages
+(code annotations part).
+"""
+
+from PyQt5.QtCore import QCoreApplication
+
+_annotationsMessages = {
+    "A001": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing type annotation for function argument '{0}'"),
+    "A002": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing type annotation for '*{0}'"),
+    "A003": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing type annotation for '**{0}'"),
+    "A101": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing type annotation for 'self' in method"),
+    "A102": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing type annotation for 'cls' in classmethod"),
+    "A201": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for public function"),
+    "A202": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for protected function"),
+    "A203": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for private function"),
+    "A204": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for special method"),
+    "A205": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for staticmethod"),
+    "A206": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "missing return type annotation for classmethod"),
+    
+    "A881": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "type annotation coverage of {0}% is too low"),
+    
+    "A891": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "type annotation is too complex ({0} > {1})"),
+    
+    "A999": QCoreApplication.translate(
+        "AnnotationsChecker",
+        "{0}: {1}"),
+}
+
+_annotationsMessagesSampleArgs = {
+    "A001": ["arg1"],
+    "A002": ["args"],
+    "A003": ["kwargs"],
+    "A881": [60],
+    "A891": [5, 3],
+    "A999": ["SyntaxError", "Invalid Syntax"],
+}
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/AnnotationsChecker.py	Mon Oct 12 19:28:42 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,465 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2019 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing a checker for function type annotations.
-"""
-
-import sys
-import ast
-
-import AstUtilities
-
-
-class AnnotationsChecker(object):
-    """
-    Class implementing a checker for function type annotations.
-    """
-    Codes = [
-        ## Function Annotations
-        "A001", "A002", "A003",
-        
-        ## Method Annotations
-        "A101", "A102",
-        
-        ## Return Annotations
-        "A201", "A202", "A203", "A204", "A205", "A206",
-        
-        ## Annotation Coverage
-        "A881",
-        
-        ## Annotation Complexity
-        "A891",
-        
-        ## Syntax Error
-        "A999",
-    ]
-
-    def __init__(self, source, filename, select, ignore, expected, repeat,
-                 args):
-        """
-        Constructor
-        
-        @param source source code to be checked
-        @type list of str
-        @param filename name of the source file
-        @type str
-        @param select list of selected codes
-        @type list of str
-        @param ignore list of codes to be ignored
-        @type list of str
-        @param expected list of expected codes
-        @type list of str
-        @param repeat flag indicating to report each occurrence of a code
-        @type bool
-        @param args dictionary of arguments for the annotation checks
-        @type dict
-        """
-        self.__select = tuple(select)
-        self.__ignore = ('',) if select else tuple(ignore)
-        self.__expected = expected[:]
-        self.__repeat = repeat
-        self.__filename = filename
-        self.__source = source[:]
-        self.__args = args
-
-        # statistics counters
-        self.counters = {}
-        
-        # collection of detected errors
-        self.errors = []
-        
-        checkersWithCodes = [
-            (
-                self.__checkFunctionAnnotations,
-                ("A001", "A002", "A003", "A101", "A102",
-                 "A201", "A202", "A203", "A204", "A205", "A206",)
-            ),
-            (self.__checkAnnotationsCoverage, ("A881",)),
-            (self.__checkAnnotationComplexity, ("A891",)),
-        ]
-        
-        self.__defaultArgs = {
-            "MinimumCoverage": 75,      # % of type annotation coverage
-            "MaximumComplexity": 3,
-        }
-        
-        self.__checkers = []
-        for checker, codes in checkersWithCodes:
-            if any(not (code and self.__ignoreCode(code))
-                    for code in codes):
-                self.__checkers.append(checker)
-    
-    def __ignoreCode(self, code):
-        """
-        Private method to check if the message code should be ignored.
-
-        @param code message code to check for
-        @type str
-        @return flag indicating to ignore the given code
-        @rtype bool
-        """
-        return (code.startswith(self.__ignore) and
-                not code.startswith(self.__select))
-    
-    def __error(self, lineNumber, offset, code, *args):
-        """
-        Private method to record an issue.
-        
-        @param lineNumber line number of the issue
-        @type int
-        @param offset position within line of the issue
-        @type int
-        @param code message code
-        @type str
-        @param args arguments for the message
-        @type list
-        """
-        if self.__ignoreCode(code):
-            return
-        
-        if code in self.counters:
-            self.counters[code] += 1
-        else:
-            self.counters[code] = 1
-        
-        # Don't care about expected codes
-        if code in self.__expected:
-            return
-        
-        if code and (self.counters[code] == 1 or self.__repeat):
-            # record the issue with one based line number
-            self.errors.append(
-                {
-                    "file": self.__filename,
-                    "line": lineNumber + 1,
-                    "offset": offset,
-                    "code": code,
-                    "args": args,
-                }
-            )
-    
-    def __reportInvalidSyntax(self):
-        """
-        Private method to report a syntax error.
-        """
-        exc_type, exc = sys.exc_info()[:2]
-        if len(exc.args) > 1:
-            offset = exc.args[1]
-            if len(offset) > 2:
-                offset = offset[1:3]
-        else:
-            offset = (1, 0)
-        self.__error(offset[0] - 1, offset[1] or 0,
-                     'A999', exc_type.__name__, exc.args[0])
-    
-    def __generateTree(self):
-        """
-        Private method to generate an AST for our source.
-        
-        @return generated AST
-        @rtype ast.Module
-        """
-        source = "".join(self.__source)
-        return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST)
-    
-    def run(self):
-        """
-        Public method to check the given source against annotation issues.
-        """
-        if not self.__filename:
-            # don't do anything, if essential data is missing
-            return
-        
-        if not self.__checkers:
-            # don't do anything, if no codes were selected
-            return
-        
-        try:
-            self.__tree = self.__generateTree()
-        except (SyntaxError, TypeError):
-            self.__reportInvalidSyntax()
-            return
-        
-        for check in self.__checkers:
-            check()
-    
-    def __checkFunctionAnnotations(self):
-        """
-        Private method to check for function annotation issues.
-        """
-        visitor = FunctionVisitor(self.__source)
-        visitor.visit(self.__tree)
-        for issue in visitor.issues:
-            node = issue[0]
-            reason = issue[1]
-            params = issue[2:]
-            self.__error(node.lineno - 1, node.col_offset, reason, *params)
-    
-    def __checkAnnotationsCoverage(self):
-        """
-        Private method to check for function annotation coverage.
-        """
-        minAnnotationsCoverage = self.__args.get(
-            "MinimumCoverage", self.__defaultArgs["MinimumCoverage"])
-        if minAnnotationsCoverage == 0:
-            # 0 means it is switched off
-            return
-        
-        functionDefs = [
-            f for f in ast.walk(self.__tree)
-            if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef))
-        ]
-        if not functionDefs:
-            # no functions/methods at all
-            return
-        
-        functionDefAnnotationsInfo = [
-            hasTypeAnnotations(f) for f in functionDefs
-        ]
-        annotationsCoverage = int(
-            len(list(filter(None, functionDefAnnotationsInfo))) /
-            len(functionDefAnnotationsInfo) * 100
-        )
-        if annotationsCoverage < minAnnotationsCoverage:
-            self.__error(0, 0, "A881", annotationsCoverage)
-    
-    def __checkAnnotationComplexity(self):
-        """
-        Private method to check the type annotation complexity.
-        """
-        maxAnnotationComplexity = self.__args.get(
-            "MaximumComplexity", self.__defaultArgs["MaximumComplexity"])
-        typeAnnotations = []
-        
-        functionDefs = [
-            f for f in ast.walk(self.__tree)
-            if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef))
-        ]
-        for functionDef in functionDefs:
-            typeAnnotations += list(filter(
-                None, [a.annotation for a in functionDef.args.args]))
-            if functionDef.returns:
-                typeAnnotations.append(functionDef.returns)
-        typeAnnotations += [a.annotation for a in ast.walk(self.__tree)
-                            if isinstance(a, ast.AnnAssign) and a.annotation]
-        for annotation in typeAnnotations:
-            complexity = getAnnotationComplexity(annotation)
-            if complexity > maxAnnotationComplexity:
-                self.__error(annotation.lineno - 1, annotation.col_offset,
-                             "A891", complexity, maxAnnotationComplexity)
-
-
-class FunctionVisitor(ast.NodeVisitor):
-    """
-    Class implementing a node visitor to check function annotations.
-    
-    Note: this class is modelled after flake8-annotations checker.
-    """
-    def __init__(self, sourceLines):
-        """
-        Constructor
-        
-        @param sourceLines lines of source code
-        @type list of str
-        """
-        super(FunctionVisitor, self).__init__()
-        
-        self.__sourceLines = sourceLines
-        
-        self.issues = []
-    
-    def visit_FunctionDef(self, node):
-        """
-        Public method to handle a function or method definition.
-        
-        @param node reference to the node to be processed
-        @type ast.FunctionDef
-        """
-        self.__checkFunctionNode(node)
-        self.generic_visit(node)
-    
-    def visit_AsyncFunctionDef(self, node):
-        """
-        Public method to handle an async function or method definition.
-        
-        @param node reference to the node to be processed
-        @type ast.AsyncFunctionDef
-        """
-        self.__checkFunctionNode(node)
-        self.generic_visit(node)
-    
-    def visit_ClassDef(self, node):
-        """
-        Public method to handle class definitions.
-        
-        @param node reference to the node to be processed
-        @type ast.ClassDef
-        """
-        methodNodes = [
-            childNode for childNode in node.body
-            if isinstance(childNode, (ast.FunctionDef, ast.AsyncFunctionDef))
-        ]
-        for methodNode in methodNodes:
-            self.__checkFunctionNode(methodNode, classMethod=True)
-    
-    def __checkFunctionNode(self, node, classMethod=False):
-        """
-        Private method to check an individual function definition node.
-        
-        @param node reference to the node to be processed
-        @type ast.FunctionDef or ast.AsyncFunctionDef
-        @param classMethod flag indicating a class method
-        @type bool
-        """
-        if node.name.startswith("__") and node.name.endswith("__"):
-            visibilityType = "special"
-        elif node.name.startswith("__"):
-            visibilityType = "private"
-        elif node.name.startswith("_"):
-            visibilityType = "protected"
-        else:
-            visibilityType = "public"
-        
-        if classMethod:
-            decorators = [
-                decorator.id for decorator in node.decorator_list
-                if isinstance(decorator, ast.Name)
-            ]
-            if "classmethod" in decorators:
-                classMethodType = "decorator"
-            elif "staticmethod" in decorators:
-                classMethodType = "staticmethod"
-            else:
-                classMethodType = ""
-        else:
-            classMethodType = "function"
-        
-        # check argument annotations
-        for argType in ("args", "vararg", "kwonlyargs", "kwarg"):
-            args = node.args.__getattribute__(argType)
-            if args:
-                if not isinstance(args, list):
-                    args = [args]
-                
-                for arg in args:
-                    if not arg.annotation:
-                        self.__classifyArgumentError(
-                            arg, argType, classMethodType)
-        
-        # check function return annotation
-        if not node.returns:
-            lineno = node.lineno
-            colOffset = self.__sourceLines[lineno - 1].rfind(":") + 1
-            self.__classifyReturnError(classMethodType, visibilityType,
-                                       lineno, colOffset)
-    
-    def __classifyReturnError(self, methodType, visibilityType, lineno,
-                              colOffset):
-        """
-        Private method to classify and record a return annotation issue.
-        
-        @param methodType type of method/function the argument belongs to
-        @type str
-        @param visibilityType visibility of the function
-        @type str
-        @param lineno line number
-        @type int
-        @param colOffset column number
-        @type int
-        """
-        # create a dummy AST node to report line and column
-        node = ast.AST()
-        node.lineno = lineno
-        node.col_offset = colOffset
-        
-        # now classify the issue
-        if methodType == "classmethod":
-            self.issues.append((node, "A206"))
-        elif methodType == "staticmethod":
-            self.issues.append((node, "A205"))
-        elif visibilityType == "special":
-            self.issues.append((node, "A204"))
-        elif visibilityType == "private":
-            self.issues.append((node, "A203"))
-        elif visibilityType == "protected":
-            self.issues.append((node, "A202"))
-        else:
-            self.issues.append((node, "A201"))
-    
-    def __classifyArgumentError(self, argNode, argType, methodType):
-        """
-        Private method to classify and record an argument annotation issue.
-        
-        @param argNode reference to the argument node
-        @type ast.arguments
-        @param argType type of the argument node
-        @type str
-        @param methodType type of method/function the argument belongs to
-        @type str
-        """
-        # check class method issues
-        if methodType != "function":
-            if argNode.arg in ("cls", "self"):
-                if methodType == "classmethod":
-                    self.issues.append((argNode, "A102"))
-                    return
-                elif methodType != "staticmethod":
-                    self.issues.append((argNode, "A101"))
-                    return
-        
-        # check all other arguments
-        if argType == "kwarg":
-            self.issues.append((argNode, "A003", argNode.arg))
-        elif argType == "vararg":
-            self.issues.append((argNode, "A002", argNode.arg))
-        else:
-            # args and kwonlyargs
-            self.issues.append((argNode, "A001", argNode.arg))
-
-######################################################################
-## some utility functions below
-######################################################################
-
-
-def hasTypeAnnotations(funcNode):
-    """
-    Function to check for type annotations.
-    
-    @param funcNode reference to the function definition node to be checked
-    @type ast.AsyncFunctionDef or ast.FunctionDef
-    @return flag indicating the presence of type annotations
-    @rtype bool
-    """
-    hasReturnAnnotation = funcNode.returns is not None
-    hasArgsAnnotations = any(a for a in funcNode.args.args
-                             if a.annotation is not None)
-    hasKwargsAnnotations = (funcNode.args and
-                            funcNode.args.kwarg and
-                            funcNode.args.kwarg.annotation is not None)
-    hasKwonlyargsAnnotations = any(a for a in funcNode.args.kwonlyargs
-                                   if a.annotation is not None)
-    
-    return any((hasReturnAnnotation, hasArgsAnnotations, hasKwargsAnnotations,
-               hasKwonlyargsAnnotations))
-
-
-def getAnnotationComplexity(annotationNode):
-    """
-    Function to determine the annotation complexity.
-    
-    @param annotationNode reference to the node to determine the annotation
-        complexity for
-    @type ast.AST
-    @return annotation complexity
-    @rtype = int
-    """
-    if AstUtilities.isString(annotationNode):
-        annotationNode = ast.parse(annotationNode.s).body[0].value
-    if isinstance(annotationNode, ast.Subscript):
-        return 1 + getAnnotationComplexity(annotationNode.slice.value)
-    if isinstance(annotationNode, ast.Tuple):
-        return max(getAnnotationComplexity(n) for n in annotationNode.elts)
-    return 1
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Mon Oct 12 19:28:42 2020 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Mon Oct 12 19:54:03 2020 +0200
@@ -20,7 +20,7 @@
 
 from DocStyleChecker import DocStyleChecker
 from MiscellaneousChecker import MiscellaneousChecker
-from ComplexityChecker import ComplexityChecker
+from Complexity.ComplexityChecker import ComplexityChecker
 from Security.SecurityChecker import SecurityChecker
 
 
@@ -418,7 +418,7 @@
             # check function annotations
             if sys.version_info >= (3, 5, 0):
                 # annotations are supported from Python 3.5 on
-                from AnnotationsChecker import AnnotationsChecker
+                from Annotations.AnnotationsChecker import AnnotationsChecker
                 annotationsChecker = AnnotationsChecker(
                     source, filename, select, ignore, [], repeatMessages,
                     annotationArgs)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2015 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a checker for code complexity.
+"""
+
+import sys
+import ast
+
+from mccabe import PathGraphingAstVisitor
+
+
+class ComplexityChecker(object):
+    """
+    Class implementing a checker for code complexity.
+    """
+    Codes = [
+        "C101",
+        "C111", "C112",
+        
+        "C901",
+    ]
+
+    def __init__(self, source, filename, select, ignore, args):
+        """
+        Constructor
+        
+        @param source source code to be checked
+        @type list of str
+        @param filename name of the source file
+        @type str
+        @param select list of selected codes
+        @type list of str
+        @param ignore list of codes to be ignored
+        @type list of str
+        @param args dictionary of arguments for the miscellaneous checks
+        @type dict
+        """
+        self.__filename = filename
+        self.__source = source[:]
+        self.__select = tuple(select)
+        self.__ignore = ('',) if select else tuple(ignore)
+        self.__args = args
+        
+        self.__defaultArgs = {
+            "McCabeComplexity": 10,
+            "LineComplexity": 15,
+            "LineComplexityScore": 10,
+        }
+        
+        # statistics counters
+        self.counters = {}
+        
+        # collection of detected errors
+        self.errors = []
+        
+        checkersWithCodes = [
+            (self.__checkMcCabeComplexity, ("C101",)),
+            (self.__checkLineComplexity, ("C111", "C112")),
+        ]
+        
+        self.__checkers = []
+        for checker, codes in checkersWithCodes:
+            if any(not (code and self.__ignoreCode(code))
+                    for code in codes):
+                self.__checkers.append(checker)
+    
+    def __ignoreCode(self, code):
+        """
+        Private method to check if the message code should be ignored.
+
+        @param code message code to check for
+        @type str
+        @return flag indicating to ignore the given code
+        @rtype bool
+        """
+        return (code.startswith(self.__ignore) and
+                not code.startswith(self.__select))
+    
+    def __error(self, lineNumber, offset, code, *args):
+        """
+        Private method to record an issue.
+        
+        @param lineNumber line number of the issue
+        @type int
+        @param offset position within line of the issue
+        @type int
+        @param code message code
+        @type str
+        @param args arguments for the message
+        @type list
+        """
+        if self.__ignoreCode(code):
+            return
+        
+        if code in self.counters:
+            self.counters[code] += 1
+        else:
+            self.counters[code] = 1
+        
+        if code:
+            # record the issue with one based line number
+            self.errors.append(
+                {
+                    "file": self.__filename,
+                    "line": lineNumber,
+                    "offset": offset,
+                    "code": code,
+                    "args": args,
+                }
+            )
+    
+    def __reportInvalidSyntax(self):
+        """
+        Private method to report a syntax error.
+        """
+        exc_type, exc = sys.exc_info()[:2]
+        if len(exc.args) > 1:
+            offset = exc.args[1]
+            if len(offset) > 2:
+                offset = offset[1:3]
+        else:
+            offset = (1, 0)
+        self.__error(offset[0] - 1, offset[1] or 0,
+                     'C901', exc_type.__name__, exc.args[0])
+    
+    def run(self):
+        """
+        Public method to check the given source for code complexity.
+        """
+        if not self.__filename or not self.__source:
+            # don't do anything, if essential data is missing
+            return
+        
+        if not self.__checkers:
+            # don't do anything, if no codes were selected
+            return
+        
+        try:
+            self.__tree = compile(''.join(self.__source), self.__filename,
+                                  'exec', ast.PyCF_ONLY_AST)
+        except (SyntaxError, TypeError):
+            self.__reportInvalidSyntax()
+            return
+        
+        for check in self.__checkers:
+            check()
+    
+    def __checkMcCabeComplexity(self):
+        """
+        Private method to check the McCabe code complexity.
+        """
+        try:
+            # create the AST again because it is modified by the checker
+            tree = compile(''.join(self.__source), self.__filename, 'exec',
+                           ast.PyCF_ONLY_AST)
+        except (SyntaxError, TypeError):
+            # compile errors are already reported by the run() method
+            return
+        
+        maxComplexity = self.__args.get("McCabeComplexity",
+                                        self.__defaultArgs["McCabeComplexity"])
+        
+        visitor = PathGraphingAstVisitor()
+        visitor.preorder(tree, visitor)
+        for graph in visitor.graphs.values():
+            if graph.complexity() > maxComplexity:
+                self.__error(graph.lineno, 0, "C101",
+                             graph.entity, graph.complexity())
+    
+    def __checkLineComplexity(self):
+        """
+        Private method to check the complexity of a single line of code and
+        the median line complexity of the source code.
+        
+        Complexity is defined as the number of AST nodes produced by a line
+        of code.
+        """
+        maxLineComplexity = self.__args.get(
+            "LineComplexity", self.__defaultArgs["LineComplexity"])
+        maxLineComplexityScore = self.__args.get(
+            "LineComplexityScore", self.__defaultArgs["LineComplexityScore"])
+        
+        visitor = LineComplexityVisitor()
+        visitor.visit(self.__tree)
+        
+        sortedItems = visitor.sortedList()
+        score = visitor.score()
+        
+        for line, complexity in sortedItems:
+            if complexity > maxLineComplexity:
+                self.__error(line, 0, "C111", complexity)
+        
+        if score > maxLineComplexityScore:
+            self.__error(0, 0, "C112", score)
+
+
+class LineComplexityVisitor(ast.NodeVisitor):
+    """
+    Class calculating the number of AST nodes per line of code
+    and the median nodes/line score.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super(LineComplexityVisitor, self).__init__()
+        self.__count = {}
+    
+    def visit(self, node):
+        """
+        Public method to recursively visit all the nodes and add up the
+        instructions.
+        
+        @param node reference to the node
+        @type ast.AST
+        """
+        if hasattr(node, 'lineno'):
+            self.__count[node.lineno] = self.__count.get(node.lineno, 0) + 1
+        self.generic_visit(node)
+    
+    def sortedList(self):
+        """
+        Public method to get a sorted list of (line, nodes) tuples.
+        
+        @return sorted list of (line, nodes) tuples
+        @rtype list of tuple of (int,int)
+        """
+        lst = [(line, self.__count[line])
+               for line in sorted(self.__count.keys())]
+        return lst
+
+    def score(self):
+        """
+        Public method to calculate the median.
+        
+        @return median line complexity value
+        @rtype float
+        """
+        lst = self.__count.values()
+        sortedList = sorted(lst)
+        listLength = len(lst)
+        medianIndex = (listLength - 1) // 2
+        
+        if listLength == 0:
+            return 0.0
+        elif (listLength % 2):
+            return float(sortedList[medianIndex])
+        else:
+            return (
+                (sortedList[medianIndex] + sortedList[medianIndex + 1]) / 2.0
+            )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/__init__.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the code complexity checker.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/translations.py	Mon Oct 12 19:54:03 2020 +0200
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+
+"""
+Module implementing message translations for the code style plugin messages
+(code complexity part).
+"""
+
+from PyQt5.QtCore import QCoreApplication
+
+_complexityMessages = {
+    "C101": QCoreApplication.translate(
+        "ComplexityChecker", "'{0}' is too complex ({1})"),
+    "C111": QCoreApplication.translate(
+        "ComplexityChecker", "source code line is too complex ({0})"),
+    "C112": QCoreApplication.translate(
+        "ComplexityChecker",
+        "overall source code line complexity is too high ({0})"),
+    "C901": QCoreApplication.translate(
+        "ComplexityChecker", "{0}: {1}"),
+}
+
+_complexityMessagesSampleArgs = {
+    "C101": ["foo.bar", "42"],
+    "C111": [42],
+    "C112": [12.0],
+    "C901": ["SyntaxError", "Invalid Syntax"],
+}
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/ComplexityChecker.py	Mon Oct 12 19:28:42 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2015 - 2020 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing a checker for code complexity.
-"""
-
-import sys
-import ast
-
-from mccabe import PathGraphingAstVisitor
-
-
-class ComplexityChecker(object):
-    """
-    Class implementing a checker for code complexity.
-    """
-    Codes = [
-        "C101",
-        "C111", "C112",
-        
-        "C901",
-    ]
-
-    def __init__(self, source, filename, select, ignore, args):
-        """
-        Constructor
-        
-        @param source source code to be checked
-        @type list of str
-        @param filename name of the source file
-        @type str
-        @param select list of selected codes
-        @type list of str
-        @param ignore list of codes to be ignored
-        @type list of str
-        @param args dictionary of arguments for the miscellaneous checks
-        @type dict
-        """
-        self.__filename = filename
-        self.__source = source[:]
-        self.__select = tuple(select)
-        self.__ignore = ('',) if select else tuple(ignore)
-        self.__args = args
-        
-        self.__defaultArgs = {
-            "McCabeComplexity": 10,
-            "LineComplexity": 15,
-            "LineComplexityScore": 10,
-        }
-        
-        # statistics counters
-        self.counters = {}
-        
-        # collection of detected errors
-        self.errors = []
-        
-        checkersWithCodes = [
-            (self.__checkMcCabeComplexity, ("C101",)),
-            (self.__checkLineComplexity, ("C111", "C112")),
-        ]
-        
-        self.__checkers = []
-        for checker, codes in checkersWithCodes:
-            if any(not (code and self.__ignoreCode(code))
-                    for code in codes):
-                self.__checkers.append(checker)
-    
-    def __ignoreCode(self, code):
-        """
-        Private method to check if the message code should be ignored.
-
-        @param code message code to check for
-        @type str
-        @return flag indicating to ignore the given code
-        @rtype bool
-        """
-        return (code.startswith(self.__ignore) and
-                not code.startswith(self.__select))
-    
-    def __error(self, lineNumber, offset, code, *args):
-        """
-        Private method to record an issue.
-        
-        @param lineNumber line number of the issue
-        @type int
-        @param offset position within line of the issue
-        @type int
-        @param code message code
-        @type str
-        @param args arguments for the message
-        @type list
-        """
-        if self.__ignoreCode(code):
-            return
-        
-        if code in self.counters:
-            self.counters[code] += 1
-        else:
-            self.counters[code] = 1
-        
-        if code:
-            # record the issue with one based line number
-            self.errors.append(
-                {
-                    "file": self.__filename,
-                    "line": lineNumber,
-                    "offset": offset,
-                    "code": code,
-                    "args": args,
-                }
-            )
-    
-    def __reportInvalidSyntax(self):
-        """
-        Private method to report a syntax error.
-        """
-        exc_type, exc = sys.exc_info()[:2]
-        if len(exc.args) > 1:
-            offset = exc.args[1]
-            if len(offset) > 2:
-                offset = offset[1:3]
-        else:
-            offset = (1, 0)
-        self.__error(offset[0] - 1, offset[1] or 0,
-                     'C901', exc_type.__name__, exc.args[0])
-    
-    def run(self):
-        """
-        Public method to check the given source for code complexity.
-        """
-        if not self.__filename or not self.__source:
-            # don't do anything, if essential data is missing
-            return
-        
-        if not self.__checkers:
-            # don't do anything, if no codes were selected
-            return
-        
-        try:
-            self.__tree = compile(''.join(self.__source), self.__filename,
-                                  'exec', ast.PyCF_ONLY_AST)
-        except (SyntaxError, TypeError):
-            self.__reportInvalidSyntax()
-            return
-        
-        for check in self.__checkers:
-            check()
-    
-    def __checkMcCabeComplexity(self):
-        """
-        Private method to check the McCabe code complexity.
-        """
-        try:
-            # create the AST again because it is modified by the checker
-            tree = compile(''.join(self.__source), self.__filename, 'exec',
-                           ast.PyCF_ONLY_AST)
-        except (SyntaxError, TypeError):
-            # compile errors are already reported by the run() method
-            return
-        
-        maxComplexity = self.__args.get("McCabeComplexity",
-                                        self.__defaultArgs["McCabeComplexity"])
-        
-        visitor = PathGraphingAstVisitor()
-        visitor.preorder(tree, visitor)
-        for graph in visitor.graphs.values():
-            if graph.complexity() > maxComplexity:
-                self.__error(graph.lineno, 0, "C101",
-                             graph.entity, graph.complexity())
-    
-    def __checkLineComplexity(self):
-        """
-        Private method to check the complexity of a single line of code and
-        the median line complexity of the source code.
-        
-        Complexity is defined as the number of AST nodes produced by a line
-        of code.
-        """
-        maxLineComplexity = self.__args.get(
-            "LineComplexity", self.__defaultArgs["LineComplexity"])
-        maxLineComplexityScore = self.__args.get(
-            "LineComplexityScore", self.__defaultArgs["LineComplexityScore"])
-        
-        visitor = LineComplexityVisitor()
-        visitor.visit(self.__tree)
-        
-        sortedItems = visitor.sortedList()
-        score = visitor.score()
-        
-        for line, complexity in sortedItems:
-            if complexity > maxLineComplexity:
-                self.__error(line, 0, "C111", complexity)
-        
-        if score > maxLineComplexityScore:
-            self.__error(0, 0, "C112", score)
-
-
-class LineComplexityVisitor(ast.NodeVisitor):
-    """
-    Class calculating the number of AST nodes per line of code
-    and the median nodes/line score.
-    """
-    def __init__(self):
-        """
-        Constructor
-        """
-        super(LineComplexityVisitor, self).__init__()
-        self.__count = {}
-    
-    def visit(self, node):
-        """
-        Public method to recursively visit all the nodes and add up the
-        instructions.
-        
-        @param node reference to the node
-        @type ast.AST
-        """
-        if hasattr(node, 'lineno'):
-            self.__count[node.lineno] = self.__count.get(node.lineno, 0) + 1
-        self.generic_visit(node)
-    
-    def sortedList(self):
-        """
-        Public method to get a sorted list of (line, nodes) tuples.
-        
-        @return sorted list of (line, nodes) tuples
-        @rtype list of tuple of (int,int)
-        """
-        lst = [(line, self.__count[line])
-               for line in sorted(self.__count.keys())]
-        return lst
-
-    def score(self):
-        """
-        Public method to calculate the median.
-        
-        @return median line complexity value
-        @rtype float
-        """
-        lst = self.__count.values()
-        sortedList = sorted(lst)
-        listLength = len(lst)
-        medianIndex = (listLength - 1) // 2
-        
-        if listLength == 0:
-            return 0.0
-        elif (listLength % 2):
-            return float(sortedList[medianIndex])
-        else:
-            return (
-                (sortedList[medianIndex] + sortedList[medianIndex + 1]) / 2.0
-            )
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/translations.py	Mon Oct 12 19:28:42 2020 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/translations.py	Mon Oct 12 19:54:03 2020 +0200
@@ -12,6 +12,12 @@
 from .Security.translations import (
     _securityMessages, _securityMessagesSampleArgs
 )
+from .Annotations.translations import (
+    _annotationsMessages, _annotationsMessagesSampleArgs
+)
+from .Complexity.translations import (
+    _complexityMessages, _complexityMessagesSampleArgs
+)
 
 # TODO: separate this huge dict into separate translations per checker type
 _messages = {
@@ -471,19 +477,6 @@
         "names 'l', 'O' and 'I' should be avoided"),
     
     ##################################################################
-    ## Code complexity messages
-    ##################################################################
-    "C101": QCoreApplication.translate(
-        "ComplexityChecker", "'{0}' is too complex ({1})"),
-    "C111": QCoreApplication.translate(
-        "ComplexityChecker", "source code line is too complex ({0})"),
-    "C112": QCoreApplication.translate(
-        "ComplexityChecker",
-        "overall source code line complexity is too high ({0})"),
-    "C901": QCoreApplication.translate(
-        "ComplexityChecker", "{0}: {1}"),
-    
-    ##################################################################
     ## Messages of the Miscellaneous Checker
     ##################################################################
     "M101": QCoreApplication.translate(
@@ -798,56 +791,6 @@
         "MiscellaneousChecker",
         "{0}: {1}"),
     
-    
-    ##################################################################
-    ## Messages of the Annotations Checker
-    ##################################################################
-    "A001": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing type annotation for function argument '{0}'"),
-    "A002": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing type annotation for '*{0}'"),
-    "A003": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing type annotation for '**{0}'"),
-    "A101": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing type annotation for 'self' in method"),
-    "A102": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing type annotation for 'cls' in classmethod"),
-    "A201": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for public function"),
-    "A202": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for protected function"),
-    "A203": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for private function"),
-    "A204": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for special method"),
-    "A205": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for staticmethod"),
-    "A206": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "missing return type annotation for classmethod"),
-    
-    "A881": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "type annotation coverage of {0}% is too low"),
-    
-    "A891": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "type annotation is too complex ({0} > {1})"),
-    
-    "A999": QCoreApplication.translate(
-        "AnnotationsChecker",
-        "{0}: {1}"),
-    
     ##################################################################
     ## CodeStyleFixer messages
     ##################################################################
@@ -1034,11 +977,6 @@
     "D263": ["buttonClicked"],
     "D901": ["SyntaxError", "Invalid Syntax"],
     
-    "C101": ["foo.bar", "42"],
-    "C111": [42],
-    "C112": [12.0],
-    "C901": ["SyntaxError", "Invalid Syntax"],
-    
     "M102": ["enc42"],
     "M131": ["list"],
     "M132": ["list"],
@@ -1064,24 +1002,21 @@
     "M823": ["dict"],
     "M901": ["SyntaxError", "Invalid Syntax"],
     
-    "A001": ["arg1"],
-    "A002": ["args"],
-    "A003": ["kwargs"],
-    "A881": [60],
-    "A891": [5, 3],
-    "A999": ["SyntaxError", "Invalid Syntax"],
-    
     "FIXWRITE_ERROR": ["IOError"],
 }
 
 messageCatalogs = (
     _messages,
     _securityMessages,
+    _annotationsMessages,
+    _complexityMessages,
 )
 
 messageSampleArgsCatalog = (
     _messages_sample_args,
     _securityMessagesSampleArgs,
+    _annotationsMessagesSampleArgs,
+    _complexityMessagesSampleArgs,
 )
 
 

eric ide

mercurial