RecursionFinder   A
last analyzed

Complexity

Total Complexity 2

Size/Duplication

Total Lines 12
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 12
rs 10
wmc 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A visit_FunctionDef() 0 3 1
A visit_Call() 0 4 1
1
"""
2
Trace object types that are inserted into Python list.
3
"""
4
import ast
5
from clike import CLikeTranspiler
6
7
8
def decltype(node):
9
    """Create C++ decltype statement"""
10
    if is_list(node):
11
        return "std::vector<decltype({0})>".format(value_type(node))
12
    else:
13
        return "decltype({0})".format(value_type(node))
14
15
16
def is_builtin_import(name):
17
    return name == "sys" or name == "math"
18
19
20
def is_list(node):
21
    """Check if a node was assigned as a list"""
22
    if isinstance(node, ast.List):
23
        return True
24
    elif isinstance(node, ast.Assign):
25
        return is_list(node.value)
26
    elif isinstance(node, ast.Name):
27
        var = node.scopes.find(node.id)
28
        return (hasattr(var, "assigned_from") and not
29
                isinstance(var.assigned_from, ast.FunctionDef) and
30
                is_list(var.assigned_from.value))
31
    else:
32
        return False
33
34
35
def value_expr(node):
36
    """
37
    Follow all assignments down the rabbit hole in order to find
38
    the value expression of a name.
39
    The boundary is set to the current scope.
40
    """
41
    return ValueExpressionVisitor().visit(node)
42
43
44
def value_type(node):
45
    """
46
    Guess the value type of a node based on the manipulations or assignments
47
    in the current scope.
48
    Special case: If node is a container like a list the value type inside the
49
    list is returned not the list type itself.
50
    """
51
    return ValueTypeVisitor().visit(node)
52
53
54
class ValueExpressionVisitor(ast.NodeVisitor):
55
    def visit_Num(self, node):
56
        return str(node.n)
57
58
    def visit_Str(self, node):
59
        return node.s
60
61
    def visit_Name(self, node):
62
        var = node.scopes.find(node.id)
63
        if isinstance(var.assigned_from, ast.For):
64
            it = var.assigned_from.iter
65
            return "std::declval<typename decltype({0})::value_type>()".format(
66
                   self.visit(it))
67
        elif isinstance(var.assigned_from, ast.FunctionDef):
68
            return var.id
69
        else:
70
            return self.visit(var.assigned_from.value)
71
72
    def visit_Call(self, node):
73
        params = ",".join([self.visit(arg) for arg in node.args])
74
        return "{0}({1})".format(node.func.id, params)
75
76
    def visit_Assign(self, node):
77
        return self.visit(node.value)
78
79
    def visit_BinOp(self, node):
80
        return "{0} {1} {2}".format(self.visit(node.left),
81
                                    CLikeTranspiler().visit(node.op),
82
                                    self.visit(node.right))
83
84
85
class ValueTypeVisitor(ast.NodeVisitor):
86
    def visit_Num(self, node):
87
        return value_expr(node)
88
89
    def visit_Str(self, node):
90
        return value_expr(node)
91
92
    def visit_Name(self, node):
93
        if node.id == 'True' or node.id == 'False':
94
            return CLikeTranspiler().visit(node)
95
96
        var = node.scopes.find(node.id)
97
        if defined_before(var, node):
98
            return node.id
99
        else:
100
            return self.visit(var.assigned_from.value)
101
102
    def visit_Call(self, node):
103
        params = ",".join([self.visit(arg) for arg in node.args])
104
        return "{0}({1})".format(node.func.id, params)
105
106
    def visit_Assign(self, node):
107
        if isinstance(node.value, ast.List):
108
            if len(node.value.elts) > 0:
109
                val = node.value.elts[0]
110
                return self.visit(val)
111
            else:
112
                target = node.targets[0]
113
                var = node.scopes.find(target.id)
114
                first_added_value = var.calls[0].args[0]
115
                return value_expr(first_added_value)
116
        else:
117
            return self.visit(node.value)
118
119
120
def defined_before(node1, node2):
121
    """Check if node a has been defined before an other node b"""
122
    return node1.lineno < node2.lineno
123
124
125
def is_list_assignment(node):
126
    return (isinstance(node.value, ast.List) and
127
            isinstance(node.targets[0].ctx, ast.Store))
128
129
130
def is_list_addition(node):
131
    """Check if operation is adding something to a list"""
132
    list_operations = ["append", "extend", "insert"]
133
    return (isinstance(node.func.ctx, ast.Load) and
134
            hasattr(node.func, "value") and
135
            isinstance(node.func.value, ast.Name) and
136
            node.func.attr in list_operations)
137
138
139
def is_recursive(fun):
140
    finder = RecursionFinder()
141
    finder.visit(fun)
142
    return finder.recursive
143
144
145
class RecursionFinder(ast.NodeVisitor):
146
    function_name = None
147
    recursive = False
148
149
    def visit_FunctionDef(self, node):
150
        self.function_name = node.name
151
        self.generic_visit(node)
152
153
    def visit_Call(self, node):
154
        self.recursive = (isinstance(node.func, ast.Name) and
155
                          node.func.id == self.function_name)
156
        self.generic_visit(node)
157