Source code for programslice.visitor

# coding: utf-8
import ast
import programslice.graph


[docs]class LineDependencyVisitor(ast.NodeVisitor): """ A visitor which creates a data dependency graph. .. note:: Read and Written variables are currently kept track of in a dictionary for each. This may cause wrong results when building the graph. Furthermore, we might want to create a data flow graph and a control flow graph. """ def __init__(self): self.graph = programslice.graph.Graph('control flow') self.writes = {} self.reads = {}
[docs] def visit_FunctionDef(self, node): """ Visits function and resets `writes` and `reads` in order to prevent that current function variables are overwritten. Note: This currently just resets writes and reads which is used to build the dependency graph. But it should set the writes and reads in such a way, so that we can slice over function boundaries. """ self.writes = {} self.reads = {} super(LineDependencyVisitor, self).generic_visit(node)
[docs] def visit_Name(self, node): """ Use the name and context in order to distinguish between variables written and read. Once the variable is read, we tie up dependencies by line number and name. """ if isinstance(node.ctx, ast.Store): self.writes.setdefault(node.id, []).append(node) elif isinstance(node.ctx, ast.Load): self.reads[node.id] = node self.connect_by_lineno(node) self.connect_by_name() elif isinstance(node.ctx, ast.Del): pass elif isinstance(node.ctx, ast.AugLoad): pass elif isinstance(node.ctx, ast.AugStore): pass elif isinstance(node.ctx, ast.Param): self.reads[node.id] = node
[docs] def connect_by_lineno(self, node): """ Connect reads and writes also by line number. We can not only connect by name, but also by line number. That ensures, that we connect the variables which are read, to assigned ones in the current line. Example: v = a + b """ for i, objs in self.writes.items(): for obj in objs: if obj.lineno == node.lineno: read = programslice.graph.Edge.create_from_astnode(obj) write = programslice.graph.Edge.create_from_astnode(node) self.graph.connect(write, read)
[docs] def connect_by_name(self): """ Here we are connecting all variables which are stored with the variables which are read. For example:: >>> v = 1 >>> b = 1 >>> c = v writes: v, c reads: v visitor visits: v (store) → b (store) → c (store) → v (read) In order to produce a graph such that: v → v → c we need to check if we can find a read variable which now is written to. Just using the line number is not good enough, because written variables may appear at the end of the function/method, not in the next line. """ for i, write_nodes in self.writes.items(): for w_node in write_nodes: r_node = self.reads.get(i) if r_node is None: continue assert isinstance(w_node.ctx, ast.Store) write = programslice.graph.Edge.create_from_astnode(w_node) read = programslice.graph.Edge.create_from_astnode(r_node) self.graph.connect(write, read)