diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py --- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py Wed Jul 13 14:55:47 2022 +0200 @@ -17,15 +17,17 @@ """ Class implementing a checker for code complexity. """ + Codes = [ "C101", - "C111", "C112", + "C111", + "C112", ] def __init__(self, source, filename, tree, select, ignore, args): """ Constructor - + @param source source code to be checked @type list of str @param filename name of the source file @@ -43,32 +45,31 @@ self.__source = source[:] self.__tree = copy.deepcopy(tree) self.__select = tuple(select) - self.__ignore = ('',) if select else tuple(ignore) + 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): + 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. @@ -78,13 +79,12 @@ @return flag indicating to ignore the given code @rtype bool """ - return (code.startswith(self.__ignore) and - not code.startswith(self.__select)) - + 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 @@ -96,12 +96,12 @@ """ 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( @@ -113,7 +113,7 @@ "args": args, } ) - + def run(self): """ Public method to check the given source for code complexity. @@ -121,59 +121,62 @@ 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 - + 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) + 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"]) - + + 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()) - + 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"]) + "LineComplexity", self.__defaultArgs["LineComplexity"] + ) maxLineComplexityScore = self.__args.get( - "LineComplexityScore", self.__defaultArgs["LineComplexityScore"]) - + "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) @@ -183,40 +186,40 @@ Class calculating the number of AST nodes per line of code and the median nodes/line score. """ + def __init__(self): """ Constructor """ super().__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'): + 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())] + 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 """ @@ -224,12 +227,10 @@ sortedList = sorted(lst) listLength = len(lst) medianIndex = (listLength - 1) // 2 - + if listLength == 0: return 0.0 - elif (listLength % 2): + elif listLength % 2: return float(sortedList[medianIndex]) else: - return ( - (sortedList[medianIndex] + sortedList[medianIndex + 1]) / 2.0 - ) + return (sortedList[medianIndex] + sortedList[medianIndex + 1]) / 2.0