RadonMetrics/radon/visitors.py

changeset 55
755bc8e1485a
parent 20
2677fbceea32
child 61
f7c284ce1e18
--- 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)

eric ide

mercurial