diff -r fb0ef164f536 -r 698ae46f40a4 eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py --- a/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py Fri Apr 02 11:59:41 2021 +0200 +++ b/eric6/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleChecker.py Sat May 01 14:27:20 2021 +0200 @@ -11,6 +11,7 @@ import ast import sys import multiprocessing +import contextlib import pycodestyle from Naming.NamingStyleChecker import NamingStyleChecker @@ -23,6 +24,7 @@ from Complexity.ComplexityChecker import ComplexityChecker from Security.SecurityChecker import SecurityChecker from PathLib.PathlibChecker import PathlibChecker +from Simplify.SimplifyChecker import SimplifyChecker def initService(): @@ -53,7 +55,7 @@ @param options options for the report (optparse.Values) """ - super(CodeStyleCheckerReport, self).__init__(options) + super().__init__(options) self.__repeat = options.repeat self.errors = [] @@ -69,7 +71,7 @@ @param args arguments for the message (list) @return error code (string) """ - code = super(CodeStyleCheckerReport, self).error_args( + code = super().error_args( line_number, offset, code, check, *args) if code and (self.counters[code] == 1 or self.__repeat): self.errors.append( @@ -278,15 +280,21 @@ @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) + @return tuple containing the error dictionary with syntax error details, + a statistics dictionary and None or a tuple containing two None and + the generated AST tree + @rtype tuple of (dict, dict, None) or tuple of (None, None, ast.Module) """ src = "".join(source) try: - ast.parse(src, filename, 'exec') - return None, None + tree = ( + 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') + ) + return None, None, tree except (SyntaxError, TypeError): exc_type, exc = sys.exc_info()[:2] if len(exc.args) > 1: @@ -295,15 +303,18 @@ 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, - }) + return ( + { + "file": filename, + "line": offset[0], + "offset": offset[1], + "code": "E901", + "args": [exc_type.__name__, exc.args[0]], + }, { + "E901": 1, + }, + None + ) def __checkCodeStyle(filename, source, args): @@ -366,13 +377,10 @@ else: ignore = [] - syntaxError, syntaxStats = __checkSyntax(filename, source) - if syntaxError: - errors = [syntaxError] - stats.update(syntaxStats) + syntaxError, syntaxStats, tree = __checkSyntax(filename, source) - # perform the checks only, if syntax is ok - else: + # 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. @@ -403,7 +411,7 @@ # miscellaneous additional checks miscellaneousChecker = MiscellaneousChecker( - source, filename, select, ignore, [], repeatMessages, + source, filename, tree, select, ignore, [], repeatMessages, miscellaneousArgs) miscellaneousChecker.run() stats.update(miscellaneousChecker.counters) @@ -411,34 +419,48 @@ # check code complexity complexityChecker = ComplexityChecker( - source, filename, 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, 5, 0): - # annotations are supported from Python 3.5 on + 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, select, ignore, [], repeatMessages, + source, filename, tree, select, ignore, [], repeatMessages, annotationArgs) annotationsChecker.run() stats.update(annotationsChecker.counters) errors += annotationsChecker.errors + # check for security issues securityChecker = SecurityChecker( - source, filename, select, ignore, [], repeatMessages, + source, filename, tree, select, ignore, [], repeatMessages, securityArgs) securityChecker.run() stats.update(securityChecker.counters) errors += securityChecker.errors + # check for pathlib usage pathlibChecker = PathlibChecker( - source, filename, 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) + simplifyChecker.run() + stats.update(simplifyChecker.counters) + errors += simplifyChecker.errors + + elif syntaxError: + errors = [syntaxError] + stats.update(syntaxStats) errorsDict = {} for error in errors: @@ -464,11 +486,9 @@ if source: code = error["code"] lineFlags = extractLineFlags(source[lineno - 1].strip()) - try: + with contextlib.suppress(IndexError): lineFlags += extractLineFlags(source[lineno].strip(), flagsLine=True) - except IndexError: - pass if securityOk(code, lineFlags): error["securityOk"] = True