1*635a8641SAndroid Build Coastguard Worker# -*- coding: utf-8 -*- 2*635a8641SAndroid Build Coastguard Worker""" 3*635a8641SAndroid Build Coastguard Worker jinja2.visitor 4*635a8641SAndroid Build Coastguard Worker ~~~~~~~~~~~~~~ 5*635a8641SAndroid Build Coastguard Worker 6*635a8641SAndroid Build Coastguard Worker This module implements a visitor for the nodes. 7*635a8641SAndroid Build Coastguard Worker 8*635a8641SAndroid Build Coastguard Worker :copyright: (c) 2017 by the Jinja Team. 9*635a8641SAndroid Build Coastguard Worker :license: BSD. 10*635a8641SAndroid Build Coastguard Worker""" 11*635a8641SAndroid Build Coastguard Workerfrom jinja2.nodes import Node 12*635a8641SAndroid Build Coastguard Worker 13*635a8641SAndroid Build Coastguard Worker 14*635a8641SAndroid Build Coastguard Workerclass NodeVisitor(object): 15*635a8641SAndroid Build Coastguard Worker """Walks the abstract syntax tree and call visitor functions for every 16*635a8641SAndroid Build Coastguard Worker node found. The visitor functions may return values which will be 17*635a8641SAndroid Build Coastguard Worker forwarded by the `visit` method. 18*635a8641SAndroid Build Coastguard Worker 19*635a8641SAndroid Build Coastguard Worker Per default the visitor functions for the nodes are ``'visit_'`` + 20*635a8641SAndroid Build Coastguard Worker class name of the node. So a `TryFinally` node visit function would 21*635a8641SAndroid Build Coastguard Worker be `visit_TryFinally`. This behavior can be changed by overriding 22*635a8641SAndroid Build Coastguard Worker the `get_visitor` function. If no visitor function exists for a node 23*635a8641SAndroid Build Coastguard Worker (return value `None`) the `generic_visit` visitor is used instead. 24*635a8641SAndroid Build Coastguard Worker """ 25*635a8641SAndroid Build Coastguard Worker 26*635a8641SAndroid Build Coastguard Worker def get_visitor(self, node): 27*635a8641SAndroid Build Coastguard Worker """Return the visitor function for this node or `None` if no visitor 28*635a8641SAndroid Build Coastguard Worker exists for this node. In that case the generic visit function is 29*635a8641SAndroid Build Coastguard Worker used instead. 30*635a8641SAndroid Build Coastguard Worker """ 31*635a8641SAndroid Build Coastguard Worker method = 'visit_' + node.__class__.__name__ 32*635a8641SAndroid Build Coastguard Worker return getattr(self, method, None) 33*635a8641SAndroid Build Coastguard Worker 34*635a8641SAndroid Build Coastguard Worker def visit(self, node, *args, **kwargs): 35*635a8641SAndroid Build Coastguard Worker """Visit a node.""" 36*635a8641SAndroid Build Coastguard Worker f = self.get_visitor(node) 37*635a8641SAndroid Build Coastguard Worker if f is not None: 38*635a8641SAndroid Build Coastguard Worker return f(node, *args, **kwargs) 39*635a8641SAndroid Build Coastguard Worker return self.generic_visit(node, *args, **kwargs) 40*635a8641SAndroid Build Coastguard Worker 41*635a8641SAndroid Build Coastguard Worker def generic_visit(self, node, *args, **kwargs): 42*635a8641SAndroid Build Coastguard Worker """Called if no explicit visitor function exists for a node.""" 43*635a8641SAndroid Build Coastguard Worker for node in node.iter_child_nodes(): 44*635a8641SAndroid Build Coastguard Worker self.visit(node, *args, **kwargs) 45*635a8641SAndroid Build Coastguard Worker 46*635a8641SAndroid Build Coastguard Worker 47*635a8641SAndroid Build Coastguard Workerclass NodeTransformer(NodeVisitor): 48*635a8641SAndroid Build Coastguard Worker """Walks the abstract syntax tree and allows modifications of nodes. 49*635a8641SAndroid Build Coastguard Worker 50*635a8641SAndroid Build Coastguard Worker The `NodeTransformer` will walk the AST and use the return value of the 51*635a8641SAndroid Build Coastguard Worker visitor functions to replace or remove the old node. If the return 52*635a8641SAndroid Build Coastguard Worker value of the visitor function is `None` the node will be removed 53*635a8641SAndroid Build Coastguard Worker from the previous location otherwise it's replaced with the return 54*635a8641SAndroid Build Coastguard Worker value. The return value may be the original node in which case no 55*635a8641SAndroid Build Coastguard Worker replacement takes place. 56*635a8641SAndroid Build Coastguard Worker """ 57*635a8641SAndroid Build Coastguard Worker 58*635a8641SAndroid Build Coastguard Worker def generic_visit(self, node, *args, **kwargs): 59*635a8641SAndroid Build Coastguard Worker for field, old_value in node.iter_fields(): 60*635a8641SAndroid Build Coastguard Worker if isinstance(old_value, list): 61*635a8641SAndroid Build Coastguard Worker new_values = [] 62*635a8641SAndroid Build Coastguard Worker for value in old_value: 63*635a8641SAndroid Build Coastguard Worker if isinstance(value, Node): 64*635a8641SAndroid Build Coastguard Worker value = self.visit(value, *args, **kwargs) 65*635a8641SAndroid Build Coastguard Worker if value is None: 66*635a8641SAndroid Build Coastguard Worker continue 67*635a8641SAndroid Build Coastguard Worker elif not isinstance(value, Node): 68*635a8641SAndroid Build Coastguard Worker new_values.extend(value) 69*635a8641SAndroid Build Coastguard Worker continue 70*635a8641SAndroid Build Coastguard Worker new_values.append(value) 71*635a8641SAndroid Build Coastguard Worker old_value[:] = new_values 72*635a8641SAndroid Build Coastguard Worker elif isinstance(old_value, Node): 73*635a8641SAndroid Build Coastguard Worker new_node = self.visit(old_value, *args, **kwargs) 74*635a8641SAndroid Build Coastguard Worker if new_node is None: 75*635a8641SAndroid Build Coastguard Worker delattr(node, field) 76*635a8641SAndroid Build Coastguard Worker else: 77*635a8641SAndroid Build Coastguard Worker setattr(node, field, new_node) 78*635a8641SAndroid Build Coastguard Worker return node 79*635a8641SAndroid Build Coastguard Worker 80*635a8641SAndroid Build Coastguard Worker def visit_list(self, node, *args, **kwargs): 81*635a8641SAndroid Build Coastguard Worker """As transformers may return lists in some places this method 82*635a8641SAndroid Build Coastguard Worker can be used to enforce a list as return value. 83*635a8641SAndroid Build Coastguard Worker """ 84*635a8641SAndroid Build Coastguard Worker rv = self.visit(node, *args, **kwargs) 85*635a8641SAndroid Build Coastguard Worker if not isinstance(rv, list): 86*635a8641SAndroid Build Coastguard Worker rv = [rv] 87*635a8641SAndroid Build Coastguard Worker return rv 88