eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py

changeset 7619
ef2b5af23ce7
parent 7615
ca2949b1a29a
child 7635
0cdead130a81
diff -r cba5c14bcd5e -r ef2b5af23ce7 eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py
--- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Mon Jun 15 18:23:27 2020 +0200
+++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py	Mon Jun 15 19:01:02 2020 +0200
@@ -12,6 +12,7 @@
 except ImportError:
     import queue
 
+import ast
 import sys
 import multiprocessing
 
@@ -272,6 +273,48 @@
         outputQueue.put((filename, result))
 
 
+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
+    @type str
+    @return tuple containing the error dictionary with syntax error details
+        and a statistics dictionary or a tuple containing two None
+    @rtype tuple of (dict, dict) or tuple of (None, None)
+    """
+    src = "".join(source)
+    # Check type for py2: if not str it's unicode
+    if sys.version_info[0] == 2:
+        try:
+            src = src.encode('utf-8')
+        except UnicodeError:
+            pass
+    
+    try:
+        ast.parse(src, filename, 'exec')
+        return None, None
+    except (SyntaxError, TypeError):
+        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)
+        return ({
+            "file": filename,
+            "line": offset[0],
+            "offset": offset[1],
+            "code": "E901",
+            "args": [exc_type.__name__, exc.args[0]],
+        }, {
+            "E901": 1,
+        })
+
+
 def __checkCodeStyle(filename, source, args):
     """
     Private module function to perform the code style check and/or fix
@@ -340,68 +383,73 @@
         else:
             ignore = []
         
-        # TODO: perform syntax check and report invalid syntax once for all
-        
-        # check coding style
-        pycodestyle.BLANK_LINES_CONFIG = {
-            # Top level class and function.
-            'top_level': blankLines[0],
-            # Methods and nested class and function.
-            'method': blankLines[1],
-        }
-        styleGuide = pycodestyle.StyleGuide(
-            reporter=CodeStyleCheckerReport,
-            repeat=repeatMessages,
-            select=select,
-            ignore=ignore,
-            max_line_length=maxLineLength,
-            max_doc_length=maxDocLineLength,
-            hang_closing=hangClosing,
-        )
-        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)
-        docStyleChecker.run()
-        stats.update(docStyleChecker.counters)
-        errors += docStyleChecker.errors
+        syntaxError, syntaxStats = __checkSyntax(filename, source)
+        if syntaxError:
+            errors = [syntaxError]
+            stats.update(syntaxStats)
         
-        # miscellaneous additional checks
-        miscellaneousChecker = MiscellaneousChecker(
-            source, filename, select, ignore, [], repeatMessages,
-            miscellaneousArgs)
-        miscellaneousChecker.run()
-        stats.update(miscellaneousChecker.counters)
-        errors += miscellaneousChecker.errors
-        
-        # check code complexity
-        complexityChecker = ComplexityChecker(
-            source, filename, select, ignore, codeComplexityArgs)
-        complexityChecker.run()
-        stats.update(complexityChecker.counters)
-        errors += complexityChecker.errors
-        
-        # check function annotations
-        if sys.version_info >= (3, 5, 0):
-            # annotations are supported from Python 3.5 on
-            from AnnotationsChecker import AnnotationsChecker
-            annotationsChecker = AnnotationsChecker(
+        # perform the checks only, if syntax is ok
+        else:
+            # check coding style
+            pycodestyle.BLANK_LINES_CONFIG = {
+                # Top level class and function.
+                'top_level': blankLines[0],
+                # Methods and nested class and function.
+                'method': blankLines[1],
+            }
+            styleGuide = pycodestyle.StyleGuide(
+                reporter=CodeStyleCheckerReport,
+                repeat=repeatMessages,
+                select=select,
+                ignore=ignore,
+                max_line_length=maxLineLength,
+                max_doc_length=maxDocLineLength,
+                hang_closing=hangClosing,
+            )
+            report = styleGuide.check_files([filename])
+            stats.update(report.counters)
+            errors = report.errors
+            
+            # check documentation style
+            docStyleChecker = DocStyleChecker(
                 source, filename, select, ignore, [], repeatMessages,
-                annotationArgs)
-            annotationsChecker.run()
-            stats.update(annotationsChecker.counters)
-            errors += annotationsChecker.errors
-        
-        securityChecker = SecurityChecker(
-            source, filename, select, ignore, [], repeatMessages,
-            securityArgs)
-        securityChecker.run()
-        stats.update(securityChecker.counters)
-        errors += securityChecker.errors
+                maxLineLength=maxDocLineLength, docType=docType)
+            docStyleChecker.run()
+            stats.update(docStyleChecker.counters)
+            errors += docStyleChecker.errors
+            
+            # miscellaneous additional checks
+            miscellaneousChecker = MiscellaneousChecker(
+                source, filename, select, ignore, [], repeatMessages,
+                miscellaneousArgs)
+            miscellaneousChecker.run()
+            stats.update(miscellaneousChecker.counters)
+            errors += miscellaneousChecker.errors
+            
+            # check code complexity
+            complexityChecker = ComplexityChecker(
+                source, filename, select, ignore, codeComplexityArgs)
+            complexityChecker.run()
+            stats.update(complexityChecker.counters)
+            errors += complexityChecker.errors
+            
+            # check function annotations
+            if sys.version_info >= (3, 5, 0):
+                # annotations are supported from Python 3.5 on
+                from AnnotationsChecker import AnnotationsChecker
+                annotationsChecker = AnnotationsChecker(
+                    source, filename, select, ignore, [], repeatMessages,
+                    annotationArgs)
+                annotationsChecker.run()
+                stats.update(annotationsChecker.counters)
+                errors += annotationsChecker.errors
+            
+            securityChecker = SecurityChecker(
+                source, filename, select, ignore, [], repeatMessages,
+                securityArgs)
+            securityChecker.run()
+            stats.update(securityChecker.counters)
+            errors += securityChecker.errors
     
     errorsDict = {}
     for error in errors:

eric ide

mercurial