src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9219
964a326c58d4
child 9233
d14a2ec006a7
--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Wed Jul 13 14:55:47 2022 +0200
@@ -31,7 +31,7 @@
 def initService():
     """
     Initialize the service and return the entry point.
-    
+
     @return the entry point for the background client (function)
     """
     return codeStyleCheck
@@ -40,7 +40,7 @@
 def initBatchService():
     """
     Initialize the batch service and return the entry point.
-    
+
     @return the entry point for the background client (function)
     """
     return codeStyleBatchCheck
@@ -50,21 +50,22 @@
     """
     Class implementing a special report to be used with our dialog.
     """
+
     def __init__(self, options):
         """
         Constructor
-        
+
         @param options options for the report (optparse.Values)
         """
         super().__init__(options)
-        
+
         self.__repeat = options.repeat
         self.errors = []
-    
+
     def error_args(self, line_number, offset, code, check, *args):
         """
         Public method to collect the error messages.
-        
+
         @param line_number line number of the issue (integer)
         @param offset position within line of the issue (integer)
         @param code message code (string)
@@ -72,8 +73,7 @@
         @param args arguments for the message (list)
         @return error code (string)
         """
-        code = super().error_args(
-            line_number, offset, code, check, *args)
+        code = super().error_args(line_number, offset, code, check, *args)
         if code and (self.counters[code] == 1 or self.__repeat):
             self.errors.append(
                 {
@@ -91,7 +91,7 @@
     """
     Function to extract flags starting and ending with '__' from a line
     comment.
-    
+
     @param line line to extract flags from (string)
     @param startComment string identifying the start of the comment (string)
     @param endComment string identifying the end of a comment (string)
@@ -99,29 +99,32 @@
     @return list containing the extracted flags (list of strings)
     """
     flags = []
-    
-    if not flagsLine or (
-       flagsLine and line.strip().startswith(startComment)):
+
+    if not flagsLine or (flagsLine and line.strip().startswith(startComment)):
         pos = line.rfind(startComment)
         if pos >= 0:
-            comment = line[pos + len(startComment):].strip()
+            comment = line[pos + len(startComment) :].strip()
             if endComment:
                 endPos = line.rfind(endComment)
                 if endPos >= 0:
                     comment = comment[:endPos]
-            flags = [f.strip() for f in comment.split()
-                     if (f.startswith("__") and f.endswith("__"))]
-            flags += [f.strip().lower() for f in comment.split()
-                      if f in ("noqa", "NOQA",
-                               "nosec", "NOSEC",
-                               "secok", "SECOK")]
+            flags = [
+                f.strip()
+                for f in comment.split()
+                if (f.startswith("__") and f.endswith("__"))
+            ]
+            flags += [
+                f.strip().lower()
+                for f in comment.split()
+                if f in ("noqa", "NOQA", "nosec", "NOSEC", "secok", "SECOK")
+            ]
     return flags
 
 
 def ignoreCode(code, lineFlags):
     """
     Function to check, if the given code should be ignored as per line flags.
-    
+
     @param code error code to be checked
     @type str
     @param lineFlags list of line flags to check against
@@ -130,29 +133,29 @@
     @rtype bool
     """
     if lineFlags:
-        
+
         if (
-            "__IGNORE_WARNING__" in lineFlags or
-            "noqa" in lineFlags or
-            "nosec" in lineFlags
+            "__IGNORE_WARNING__" in lineFlags
+            or "noqa" in lineFlags
+            or "nosec" in lineFlags
         ):
             # ignore all warning codes
             return True
-        
+
         for flag in lineFlags:
             # check individual warning code
             if flag.startswith("__IGNORE_WARNING_"):
                 ignoredCode = flag[2:-2].rsplit("_", 1)[-1]
                 if code.startswith(ignoredCode):
                     return True
-    
+
     return False
 
 
 def securityOk(code, lineFlags):
     """
     Function to check, if the given code is an acknowledged security report.
-    
+
     @param code error code to be checked
     @type str
     @param lineFlags list of line flags to check against
@@ -162,14 +165,14 @@
     """
     if lineFlags:
         return "secok" in lineFlags
-    
+
     return False
 
 
 def codeStyleCheck(filename, source, args):
     """
     Do the code style check and/or fix found errors.
-    
+
     @param filename source filename
     @type str
     @param source string containing the code to check
@@ -194,7 +197,7 @@
 def codeStyleBatchCheck(argumentsList, send, fx, cancelled, maxProcesses=0):
     """
     Module function to check code style for a batch of files.
-    
+
     @param argumentsList list of arguments tuples as given for codeStyleCheck
     @type list
     @param send reference to send function
@@ -228,9 +231,8 @@
 
     # Start worker processes
     workers = [
-        multiprocessing.Process(
-            target=workerTask, args=(taskQueue, doneQueue)
-        ) for _ in range(NumberOfProcesses)
+        multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue))
+        for _ in range(NumberOfProcesses)
     ]
     for worker in workers:
         worker.start()
@@ -240,7 +242,7 @@
     for i in range(len(argumentsList)):
         resultSent = False
         wasCancelled = False
-        
+
         while not resultSent:
             try:
                 # get result (waiting max. 3 seconds and send it to frontend
@@ -252,18 +254,18 @@
                 if cancelled():
                     wasCancelled = True
                     break
-        
+
         if wasCancelled or cancelled():
             # just exit the loop ignoring the results of queued tasks
             break
-        
+
         if i < endIndex:
             taskQueue.put(argumentsList.pop(0))
 
     # Tell child processes to stop
     for _ in range(NumberOfProcesses):
-        taskQueue.put('STOP')
-    
+        taskQueue.put("STOP")
+
     for worker in workers:
         worker.join()
         worker.close()
@@ -272,11 +274,11 @@
 def workerTask(inputQueue, outputQueue):
     """
     Module function acting as the parallel worker for the style check.
-    
+
     @param inputQueue input queue (multiprocessing.Queue)
     @param outputQueue output queue (multiprocessing.Queue)
     """
-    for filename, source, args in iter(inputQueue.get, 'STOP'):
+    for filename, source, args in iter(inputQueue.get, "STOP"):
         result = __checkCodeStyle(filename, source, args)
         outputQueue.put((filename, result))
 
@@ -284,7 +286,7 @@
 def __checkSyntax(filename, source):
     """
     Private module function to perform a syntax check.
-    
+
     @param filename source filename
     @type str
     @param source string containing the code to check
@@ -295,13 +297,13 @@
     @rtype tuple of (dict, dict, None) or tuple of (None, None, ast.Module)
     """
     src = "".join(source)
-    
+
     try:
         tree = (
-            ast.parse(src, filename, 'exec', type_comments=True)
+            ast.parse(src, filename, "exec", type_comments=True)
             # need the 'type_comments' parameter to include type annotations
-            if sys.version_info >= (3, 8) else
-            ast.parse(src, filename, 'exec')
+            if sys.version_info >= (3, 8)
+            else ast.parse(src, filename, "exec")
         )
         return None, None, tree
     except (SyntaxError, TypeError):
@@ -319,10 +321,11 @@
                 "offset": offset[1],
                 "code": "E901",
                 "args": [exc_type.__name__, exc.args[0]],
-            }, {
+            },
+            {
                 "E901": 1,
             },
-            None
+            None,
         )
 
 
@@ -330,7 +333,7 @@
     """
     Private module function to perform the code style check and/or fix
     found errors.
-    
+
     @param filename source filename
     @type str
     @param source string containing the code to check
@@ -359,44 +362,69 @@
         </ul>
     @rtype tuple of (dict, list of dict)
     """
-    (excludeMessages, includeMessages, repeatMessages, fixCodes, noFixCodes,
-     fixIssues, maxLineLength, maxDocLineLength, blankLines, hangClosing,
-     docType, codeComplexityArgs, miscellaneousArgs, annotationArgs,
-     securityArgs, importsArgs, errors, eol, encoding, backup) = args
-    
+    (
+        excludeMessages,
+        includeMessages,
+        repeatMessages,
+        fixCodes,
+        noFixCodes,
+        fixIssues,
+        maxLineLength,
+        maxDocLineLength,
+        blankLines,
+        hangClosing,
+        docType,
+        codeComplexityArgs,
+        miscellaneousArgs,
+        annotationArgs,
+        securityArgs,
+        importsArgs,
+        errors,
+        eol,
+        encoding,
+        backup,
+    ) = args
+
     stats = {}
 
     if fixIssues:
         from CodeStyleFixer import CodeStyleFixer
+
         fixer = CodeStyleFixer(
-            filename, source, fixCodes, noFixCodes,
-            maxLineLength, blankLines, True, eol, backup)
+            filename,
+            source,
+            fixCodes,
+            noFixCodes,
+            maxLineLength,
+            blankLines,
+            True,
+            eol,
+            backup,
+        )
         # always fix in place
     else:
         fixer = None
-    
+
     if not errors:
         if includeMessages:
-            select = [s.strip() for s in
-                      includeMessages.split(',') if s.strip()]
+            select = [s.strip() for s in includeMessages.split(",") if s.strip()]
         else:
             select = []
         if excludeMessages:
-            ignore = [i.strip() for i in
-                      excludeMessages.split(',') if i.strip()]
+            ignore = [i.strip() for i in excludeMessages.split(",") if i.strip()]
         else:
             ignore = []
-        
+
         syntaxError, syntaxStats, tree = __checkSyntax(filename, source)
-        
+
         # perform the checks only, if syntax is ok and AST tree was generated
         if tree:
             # check coding style
             pycodestyle.BLANK_LINES_CONFIG = {
                 # Top level class and function.
-                'top_level': blankLines[0],
+                "top_level": blankLines[0],
                 # Methods and nested class and function.
-                'method': blankLines[1],
+                "method": blankLines[1],
             }
             styleGuide = pycodestyle.StyleGuide(
                 reporter=CodeStyleCheckerReport,
@@ -410,76 +438,101 @@
             report = styleGuide.check_files([filename])
             stats.update(report.counters)
             errors = report.errors
-            
+
             # check documentation style
             docStyleChecker = DocStyleChecker(
-                source, filename, select, ignore, [], repeatMessages,
-                maxLineLength=maxDocLineLength, docType=docType)
+                source,
+                filename,
+                select,
+                ignore,
+                [],
+                repeatMessages,
+                maxLineLength=maxDocLineLength,
+                docType=docType,
+            )
             docStyleChecker.run()
             stats.update(docStyleChecker.counters)
             errors += docStyleChecker.errors
-            
+
             # miscellaneous additional checks
             miscellaneousChecker = MiscellaneousChecker(
-                source, filename, tree, select, ignore, [], repeatMessages,
-                miscellaneousArgs)
+                source,
+                filename,
+                tree,
+                select,
+                ignore,
+                [],
+                repeatMessages,
+                miscellaneousArgs,
+            )
             miscellaneousChecker.run()
             stats.update(miscellaneousChecker.counters)
             errors += miscellaneousChecker.errors
-            
+
             # check code complexity
             complexityChecker = ComplexityChecker(
-                source, filename, tree, select, ignore, codeComplexityArgs)
+                source, filename, tree, select, ignore, codeComplexityArgs
+            )
             complexityChecker.run()
             stats.update(complexityChecker.counters)
             errors += complexityChecker.errors
-            
+
             # check function annotations
             if sys.version_info >= (3, 8, 0):
                 # annotations with type comments are supported from
                 # Python 3.8 on
                 from Annotations.AnnotationsChecker import AnnotationsChecker
+
                 annotationsChecker = AnnotationsChecker(
-                    source, filename, tree, select, ignore, [], repeatMessages,
-                    annotationArgs)
+                    source,
+                    filename,
+                    tree,
+                    select,
+                    ignore,
+                    [],
+                    repeatMessages,
+                    annotationArgs,
+                )
                 annotationsChecker.run()
                 stats.update(annotationsChecker.counters)
                 errors += annotationsChecker.errors
-            
+
             # check for security issues
             securityChecker = SecurityChecker(
-                source, filename, tree, select, ignore, [], repeatMessages,
-                securityArgs)
+                source, filename, tree, select, ignore, [], repeatMessages, securityArgs
+            )
             securityChecker.run()
             stats.update(securityChecker.counters)
             errors += securityChecker.errors
-            
+
             # check for pathlib usage
             pathlibChecker = PathlibChecker(
-                source, filename, tree, select, ignore, [], repeatMessages)
+                source, filename, tree, select, ignore, [], repeatMessages
+            )
             pathlibChecker.run()
             stats.update(pathlibChecker.counters)
             errors += pathlibChecker.errors
-            
+
             # check for code simplifications
             simplifyChecker = SimplifyChecker(
-                source, filename, tree, select, ignore, [], repeatMessages)
+                source, filename, tree, select, ignore, [], repeatMessages
+            )
             simplifyChecker.run()
             stats.update(simplifyChecker.counters)
             errors += simplifyChecker.errors
-            
+
             # check import statements
             importsChecker = ImportsChecker(
-                source, filename, tree, select, ignore, [], repeatMessages,
-                importsArgs)
+                source, filename, tree, select, ignore, [], repeatMessages, importsArgs
+            )
             importsChecker.run()
             stats.update(importsChecker.counters)
             errors += importsChecker.errors
-        
+
         elif syntaxError:
             errors = [syntaxError]
             stats.update(syntaxStats)
-    
+
     errorsDict = {}
     for error in errors:
         if error["line"] > len(source):
@@ -492,62 +545,72 @@
     for lineno, errorsList in errorsDict.items():
         errorsList.sort(key=lambda x: x[0], reverse=True)
         for _, error in errorsList:
-            error.update({
-                "ignored": False,
-                "fixed": False,
-                "autofixing": False,
-                "fixcode": "",
-                "fixargs": [],
-                "securityOk": False,
-            })
-            
+            error.update(
+                {
+                    "ignored": False,
+                    "fixed": False,
+                    "autofixing": False,
+                    "fixcode": "",
+                    "fixargs": [],
+                    "securityOk": False,
+                }
+            )
+
             if source:
                 code = error["code"]
                 lineFlags = extractLineFlags(source[lineno - 1].strip())
                 with contextlib.suppress(IndexError):
-                    lineFlags += extractLineFlags(source[lineno].strip(),
-                                                  flagsLine=True)
-                
+                    lineFlags += extractLineFlags(
+                        source[lineno].strip(), flagsLine=True
+                    )
+
                 if securityOk(code, lineFlags):
                     error["securityOk"] = True
-                
+
                 if ignoreCode(code, lineFlags):
                     error["ignored"] = True
                 else:
                     if fixer:
                         res, fixcode, fixargs, id_ = fixer.fixIssue(
-                            lineno, error["offset"], code)
+                            lineno, error["offset"], code
+                        )
                         if res == -1:
                             deferredFixes[id_] = error
                         else:
-                            error.update({
-                                "fixed": res == 1,
-                                "autofixing": True,
-                                "fixcode": fixcode,
-                                "fixargs": fixargs,
-                            })
-            
+                            error.update(
+                                {
+                                    "fixed": res == 1,
+                                    "autofixing": True,
+                                    "fixcode": fixcode,
+                                    "fixargs": fixargs,
+                                }
+                            )
+
             results.append(error)
-    
+
     if fixer:
         deferredResults = fixer.finalize()
         for id_ in deferredResults:
             fixed, fixcode, fixargs = deferredResults[id_]
             error = deferredFixes[id_]
-            error.update({
-                "ignored": False,
-                "fixed": fixed == 1,
-                "autofixing": True,
-                "fixcode": fixcode,
-                "fixargs": fixargs,
-            })
+            error.update(
+                {
+                    "ignored": False,
+                    "fixed": fixed == 1,
+                    "autofixing": True,
+                    "fixcode": fixcode,
+                    "fixargs": fixargs,
+                }
+            )
 
         saveError = fixer.saveFile(encoding)
         if saveError:
             for error in results:
-                error.update({
-                    "fixcode": saveError[0],
-                    "fixargs": saveError[1],
-                })
+                error.update(
+                    {
+                        "fixcode": saveError[0],
+                        "fixargs": saveError[1],
+                    }
+                )
 
     return stats, results

eric ide

mercurial