Source code for pysqllike.translation.node_visitor_translator

"""
One class which visits a syntax tree.


:githublink:`%|py|5`
"""

import ast


[docs]class CodeNodeVisitor(ast.NodeVisitor): """ Defines a visitor which walks though the syntax tree of the code. .. exref:: :title: Get the tree of a simple function The following code uses Python syntax but follows a SQL logic. .. runpython:: :showcode: :process: import ast import inspect import textwrap from pysqllike.translation.node_visitor_translator import CodeNodeVisitor def myjob(input): iter = input.select (input.age, input.nom, age2 = input.age2*input.age2) wher = iter.where( (iter.age > 60).Or(iter.age < 25)) return wher code = textwrap.dedent(inspect.getsource(myjob)) node = ast.parse(code) v = CodeNodeVisitor() v.visit(node) for r in v.Rows : print("{0}{1}: {2}".format(" " * r["indent"], r["type"], r["str"])) :githublink:`%|py|39` """
[docs] def __init__(self): """ constructor :githublink:`%|py|44` """ ast.NodeVisitor.__init__(self) self._rows = [] self._indent = 0 self._stack = []
[docs] def push(self, row): """ Pushes an element into a list. :githublink:`%|py|53` """ self._rows.append(row)
[docs] def generic_visit(self, node): """ Overrides ``generic_visit`` to check it is not used. :githublink:`%|py|59` """ raise AttributeError("generic_visit_args should be used.")
[docs] def generic_visit_args(self, node, row): """ Overrides ``generic_visit`` to keep track of the indentation and the node parent. The function will add field ``row["children"] = visited`` nodes from here. :param node: node which needs to be visited :param row: row (a dictionary) :return: See ``ast.NodeVisitor.generic_visit`` :githublink:`%|py|71` """ self._indent += 1 last = len(self._rows) res = ast.NodeVisitor.generic_visit(self, node) row["children"] = [ _ for _ in self._rows[ last:] if _["indent"] == self._indent] self._indent -= 1 return res
[docs] def visit(self, node): """ Visits a node, a method must exist for every object class. :githublink:`%|py|84` """ method = 'visit_' + node.__class__.__name__ visitor = getattr(self, method, None) if visitor is None: raise TypeError("unable to find a method: " + method) res = visitor(node) # print(method, CodeNodeVisitor.print_node(node)) return res
[docs] @staticmethod def print_node(node): """ Debugging purpose. :githublink:`%|py|97` """ r = [] for att in ["s", "name", "str", "id", "body", "n", "arg", "targets", "attr", "returns", "ctx"]: if att in node.__dict__: r.append("{0}={1}".format(att, str(node.__dict__[att]))) return " ".join(r)
[docs] def print_tree(self): """ Displays the tree of instructions. :return: string :githublink:`%|py|110` """ rows = [] for r in self.Rows: rows.append( ("{0}{1}: {2}".format( " " * r["indent"], r["type"], r["str"]))) return "\n".join(rows)
@property def Rows(self): """ returns a list of dictionaries with all the elements of the code :githublink:`%|py|125` """ return [_ for _ in self._rows if not _.get("remove", False)] def visit_Str(self, node): cont = { "indent": self._indent, "type": "Str", "str": node.s, "node": node, "value": node.s} self.push(cont) return self.generic_visit_args(node, cont) def visit_Name(self, node): cont = { "indent": self._indent, "type": "Name", "str": node.id, "node": node, "id": node.id, "ctx": node.ctx} self.push(cont) return self.generic_visit_args(node, cont) def visit_Module(self, node): cont = { "indent": self._indent, "type": "Module", "str": "", "body": node.body, "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_FunctionDef(self, node): cont = {"indent": self._indent, "type": "FunctionDef", "str": node.name, "name": node.name, "body": node.body, "node": node, "returns": node.returns} self.push(cont) return self.generic_visit_args(node, cont) def visit_arguments(self, node): cont = {"indent": self._indent, "type": "arguments", "str": "", "node": node, "args": node.args} self.push(cont) return self.generic_visit_args(node, cont) def visit_arg(self, node): cont = {"indent": self._indent, "type": "arg", "str": node.arg, "node": node, "arg": node.arg, "annotation": node.annotation} self.push(cont) return self.generic_visit_args(node, cont) def visit_Assign(self, node): cont = {"indent": self._indent, "type": "Assign", "str": "", "node": node, "targets": node.targets, "value": node.value} self.push(cont) return self.generic_visit_args(node, cont) def visit_Store(self, node): #cont = { "indent":self._indent, "type": "Store", "str": "" } # self.push(cont) cont = {} return self.generic_visit_args(node, cont) def visit_Call(self, node): if "attr" in node.func.__dict__: cont = {"indent": self._indent, "type": "Call", "str": node.func.attr, "node": node, "func": node.func} else: cont = {"indent": self._indent, "type": "Call", "str": node.func.id, "node": node, "func": node.func} self.push(cont) return self.generic_visit_args(node, cont) def visit_Attribute(self, node): cont = {"indent": self._indent, "type": "Attribute", "str": node.attr, "node": node, "value": node.value, "ctx": node.ctx, "attr": node.attr} self.push(cont) # last = len(self._rows) res = self.generic_visit_args(node, cont) if len(cont["children"]) > 0: fir = cont["children"][0] if fir["type"] == "Name": parent = fir["node"].id cont["str"] = "{0}.{1}".format(parent, cont["str"]) cont["children"][0]["remove"] = True return res def visit_Load(self, node): #cont = { "indent":self._indent, "type": "Load", "str": "" } # self.push(cont) cont = {} return self.generic_visit_args(node, cont) def visit_keyword(self, node): cont = {"indent": self._indent, "type": "keyword", "str": "{0}".format(node.arg), "node": node, "arg": node.arg, "value": node.value} self.push(cont) return self.generic_visit_args(node, cont) def visit_BinOp(self, node): cont = { "indent": self._indent, "type": "BinOp", "str": "", "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_Mult(self, node): cont = { "indent": self._indent, "type": "Mult", "str": "", "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_Compare(self, node): cont = { "indent": self._indent, "type": "Compare", "str": "", "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_Gt(self, node): cont = {"indent": self._indent, "type": "Gt", "str": "", "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_Lt(self, node): cont = {"indent": self._indent, "type": "Lt", "str": "", "node": node} self.push(cont) return self.generic_visit_args(node, cont) def visit_Num(self, node): cont = { "indent": self._indent, "type": "Num", "node": node, "str": "{0}".format( node.n), 'n': node.n} self.push(cont) return self.generic_visit_args(node, cont) def visit_Return(self, node): cont = {"indent": self._indent, "type": "Return", "node": node, "str": "", 'value': node.value} self.push(cont) return self.generic_visit_args(node, cont) def visit_(self, node): help(node) assert False