--- a/RadonMetrics/radon/visitors.py Sat Mar 31 13:05:51 2018 +0200 +++ b/RadonMetrics/radon/visitors.py Sun Nov 25 18:32:27 2018 +0100 @@ -2,10 +2,6 @@ analysis concerning Cyclomatic Complexity is done. There is also the class HalsteadVisitor, that counts Halstead metrics.''' -# -# patched to support Python 3.5 'async def'. -# - import ast import operator import collections @@ -23,17 +19,16 @@ 'complexity']) BaseClass = collections.namedtuple('Class', ['name', 'lineno', 'col_offset', 'endline', 'methods', + 'inner_classes', 'real_complexity']) def code2ast(source): - '''Convert a string object into an AST object. This function attempts to - convert the string into bytes. + '''Convert a string object into an AST object. + + This function is retained for backwards compatibility, but it no longer + attemps any conversions. It's equivalent to a call to ``ast.parse``. ''' - try: - source = source.encode('utf-8') # necessary in Python 3 - except UnicodeDecodeError: # pragma: no cover - pass return ast.parse(source) @@ -211,7 +206,7 @@ self.complexity += len(node.values) - 1 # Ifs, with and assert statements count all as 1. # Note: Lambda functions are not counted anymore, see #68 - elif name in ('With', 'If', 'IfExp', 'AsyncWith'): + elif name in ('With', 'If', 'IfExp', 'AsyncWith'): self.complexity += 1 # The For and While blocks count as 1 plus the `else` block. elif name in ('For', 'While', 'AsyncFor'): @@ -229,6 +224,12 @@ ''' self.complexity += not self.no_assert + def visit_AsyncFunctionDef(self, node): + '''Async function definition is the same thing as the synchronous + one. + ''' + self.visit_FunctionDef(node) + def visit_FunctionDef(self, node): '''When visiting functions a new visitor is created to recursively analyze the function's body. @@ -251,8 +252,6 @@ self.classname, closures, body_complexity) self.functions.append(func) - visit_AsyncFunctionDef = visit_FunctionDef - def visit_ClassDef(self, node): '''When visiting classes a new visitor is created to recursively analyze the class' body and methods. @@ -266,6 +265,7 @@ body_complexity = 1 classname = node.name visitors_max_lines = [node.lineno] + inner_classes = [] for child in node.body: visitor = ComplexityVisitor(True, classname, off=False, no_assert=self.no_assert) @@ -274,10 +274,11 @@ body_complexity += (visitor.complexity + visitor.functions_complexity) visitors_max_lines.append(visitor.max_line) + inner_classes.extend(visitor.classes) cls = Class(classname, node.lineno, node.col_offset, max(visitors_max_lines + list(map(GET_ENDLINE, methods))), - methods, body_complexity) + methods, inner_classes, body_complexity) self.classes.append(cls) @@ -298,6 +299,9 @@ self.operands = 0 self.context = context + # A new visitor is spawned for every scanned function. + self.function_visitors = [] + @property def distinct_operators(self): '''The number of distinct operators.''' @@ -362,8 +366,11 @@ def visit_FunctionDef(self, node): '''When visiting functions, another visitor is created to recursively - analyze the function's body. + analyze the function's body. We also track information on the function + itself. ''' + func_visitor = HalsteadVisitor(context=node.name) + for child in node.body: visitor = HalsteadVisitor.from_ast(child, context=node.name) self.operators += visitor.operators @@ -371,4 +378,10 @@ self.operators_seen.update(visitor.operators_seen) self.operands_seen.update(visitor.operands_seen) - visit_AsyncFunctionDef = visit_FunctionDef + func_visitor.operators += visitor.operators + func_visitor.operands += visitor.operands + func_visitor.operators_seen.update(visitor.operators_seen) + func_visitor.operands_seen.update(visitor.operands_seen) + + # Save the visited function visitor for later reference. + self.function_visitors.append(func_visitor)