# -*- coding: utf-8 -*- """ jinja.nodes ~~~~~~~~~~~ This module implements additional nodes derived from the ast base node. It also provides some node tree helper functions like `in_lineno` and `get_nodes` used by the parser and translator in order to normalize python and jinja nodes. :copyright: 2007 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from itertools import chain from copy import copy def get_nodes(nodetype, tree, exclude_root=True): """ Get all nodes from nodetype in the tree excluding the node passed if `exclude_root` is `True` (default). """ if exclude_root: todo = tree.get_child_nodes() else: todo = [tree] while todo: node = todo.pop() if node.__class__ is nodetype: yield node todo.extend(node.get_child_nodes()) class NotPossible(NotImplementedError): """ If a given node cannot do something. """ class Node(object): """ Jinja node. """ def __init__(self, lineno=None, filename=None): self.lineno = lineno self.filename = filename def get_items(self): return [] def get_child_nodes(self): return [x for x in self.get_items() if isinstance(x, Node)] def allows_assignments(self): return False def __repr__(self): return 'Node()' class Text(Node): """ Node that represents normal text. """ def __init__(self, text, variables, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.text = text self.variables = variables def get_items(self): return [self.text] + list(self.variables) def __repr__(self): return 'Text(%r, %r)' % ( self.text, self.variables ) class NodeList(list, Node): """ A node that stores multiple childnodes. """ def __init__(self, data, lineno=None, filename=None): Node.__init__(self, lineno, filename) list.__init__(self, data) def get_items(self): return list(self) def __repr__(self): return 'NodeList(%s)' % list.__repr__(self) class Template(Node): """ Node that represents a template. """ def __init__(self, extends, body, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.extends = extends self.body = body def get_items(self): return [self.extends, self.body] def __repr__(self): return 'Template(%r, %r)' % ( self.extends, self.body ) class ForLoop(Node): """ A node that represents a for loop """ def __init__(self, item, seq, body, else_, recursive, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.item = item self.seq = seq self.body = body self.else_ = else_ self.recursive = recursive def get_items(self): return [self.item, self.seq, self.body, self.else_, self.recursive] def __repr__(self): return 'ForLoop(%r, %r, %r, %r, %r)' % ( self.item, self.seq, self.body, self.else_, self.recursive ) class IfCondition(Node): """ A node that represents an if condition. """ def __init__(self, tests, else_, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.tests = tests self.else_ = else_ def get_items(self): result = [] for test in self.tests: result.extend(test) result.append(self.else_) return result def __repr__(self): return 'IfCondition(%r, %r)' % ( self.tests, self.else_ ) class Cycle(Node): """ A node that represents the cycle statement. """ def __init__(self, seq, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.seq = seq def get_items(self): return [self.seq] def __repr__(self): return 'Cycle(%r)' % (self.seq,) class Print(Node): """ A node that represents variable tags and print calls. """ def __init__(self, expr, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.expr = expr def get_items(self): return [self.expr] def __repr__(self): return 'Print(%r)' % (self.expr,) class Macro(Node): """ A node that represents a macro. """ def __init__(self, name, arguments, body, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.name = name self.arguments = arguments self.body = body def get_items(self): return [self.name] + list(chain(*self.arguments)) + [self.body] def __repr__(self): return 'Macro(%r, %r, %r)' % ( self.name, self.arguments, self.body ) class Call(Node): """ A node that represents am extended macro call. """ def __init__(self, expr, body, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.expr = expr self.body = body def get_items(self): return [self.expr, self.body] def __repr__(self): return 'Call(%r, %r)' % ( self.expr, self.body ) class Set(Node): """ Allows defining own variables. """ def __init__(self, name, expr, scope_local, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.name = name self.expr = expr self.scope_local = scope_local def get_items(self): return [self.name, self.expr, self.scope_local] def __repr__(self): return 'Set(%r, %r, %r)' % ( self.name, self.expr, self.scope_local ) class Filter(Node): """ Node for filter sections. """ def __init__(self, body, filters, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.body = body self.filters = filters def get_items(self): return [self.body] + list(self.filters) def __repr__(self): return 'Filter(%r, %r)' % ( self.body, self.filters ) class Block(Node): """ A node that represents a block. """ def __init__(self, name, body, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.name = name self.body = body def replace(self, node): """ Replace the current data with the copied data of another block node. """ assert node.__class__ is Block self.lineno = node.lineno self.filename = node.filename self.name = node.name self.body = copy(node.body) def clone(self): """ Create an independent clone of this node. """ return copy(self) def get_items(self): return [self.name, self.body] def __repr__(self): return 'Block(%r, %r)' % ( self.name, self.body ) class Include(Node): """ A node that represents the include tag. """ def __init__(self, template, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.template = template def get_items(self): return [self.template] def __repr__(self): return 'Include(%r)' % ( self.template ) class Trans(Node): """ A node for translatable sections. """ def __init__(self, singular, plural, indicator, replacements, lineno=None, filename=None): Node.__init__(self, lineno, filename) self.singular = singular self.plural = plural self.indicator = indicator self.replacements = replacements def get_items(self): rv = [self.singular, self.plural, self.indicator] if self.replacements: rv.extend(self.replacements.values()) rv.extend(self.replacements.keys()) return rv def __repr__(self): return 'Trans(%r, %r, %r, %r)' % ( self.singular, self.plural, self.indicator, self.replacements ) class Expression(Node): """ Baseclass for all expressions. """ class BinaryExpression(Expression): """ Baseclass for all binary expressions. """ def __init__(self, left, right, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.left = left self.right = right def get_items(self): return [self.left, self.right] def __repr__(self): return '%s(%r, %r)' % ( self.__class__.__name__, self.left, self.right ) class UnaryExpression(Expression): """ Baseclass for all unary expressions. """ def __init__(self, node, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.node = node def get_items(self): return [self.node] def __repr__(self): return '%s(%r)' % ( self.__class__.__name__, self.node ) class ConstantExpression(Expression): """ any constat such as {{ "foo" }} """ def __init__(self, value, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.value = value def get_items(self): return [self.value] def __repr__(self): return 'ConstantExpression(%r)' % (self.value,) class UndefinedExpression(Expression): """ represents the special 'undefined' value. """ def __repr__(self): return 'UndefinedExpression()' class RegexExpression(Expression): """ represents the regular expression literal. """ def __init__(self, value, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.value = value def get_items(self): return [self.value] def __repr__(self): return 'RegexExpression(%r)' % (self.value,) class NameExpression(Expression): """ any name such as {{ foo }} """ def __init__(self, name, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.name = name def get_items(self): return [self.name] def allows_assignments(self): return self.name != '_' def __repr__(self): return 'NameExpression(%r)' % self.name class ListExpression(Expression): """ any list literal such as {{ [1, 2, 3] }} """ def __init__(self, items, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.items = items def get_items(self): return list(self.items) def __repr__(self): return 'ListExpression(%r)' % (self.items,) class DictExpression(Expression): """ any dict literal such as {{ {1: 2, 3: 4} }} """ def __init__(self, items, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.items = items def get_items(self): return list(chain(*self.items)) def __repr__(self): return 'DictExpression(%r)' % (self.items,) class SetExpression(Expression): """ any set literal such as {{ @(1, 2, 3) }} """ def __init__(self, items, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.items = items def get_items(self): return self.items[:] def __repr__(self): return 'SetExpression(%r)' % (self.items,) class ConditionalExpression(Expression): """ {{ foo if bar else baz }} """ def __init__(self, test, expr1, expr2, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.test = test self.expr1 = expr1 self.expr2 = expr2 def get_items(self): return [self.test, self.expr1, self.expr2] def __repr__(self): return 'ConstantExpression(%r, %r, %r)' % ( self.test, self.expr1, self.expr2 ) class FilterExpression(Expression): """ {{ foo|bar|baz }} """ def __init__(self, node, filters, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.node = node self.filters = filters def get_items(self): result = [self.node] for filter, args in self.filters: result.append(filter) result.extend(args) return result def __repr__(self): return 'FilterExpression(%r, %r)' % ( self.node, self.filters ) class TestExpression(Expression): """ {{ foo is lower }} """ def __init__(self, node, name, args, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.node = node self.name = name self.args = args def get_items(self): return [self.node, self.name] + list(self.args) def __repr__(self): return 'TestExpression(%r, %r, %r)' % ( self.node, self.name, self.args ) class CallExpression(Expression): """ {{ foo(bar) }} """ def __init__(self, node, args, kwargs, dyn_args, dyn_kwargs, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.node = node self.args = args self.kwargs = kwargs self.dyn_args = dyn_args self.dyn_kwargs = dyn_kwargs def get_items(self): return [self.node, self.args, self.kwargs, self.dyn_args, self.dyn_kwargs] def __repr__(self): return 'CallExpression(%r, %r, %r, %r, %r)' % ( self.node, self.args, self.kwargs, self.dyn_args, self.dyn_kwargs ) class SubscriptExpression(Expression): """ {{ foo.bar }} and {{ foo['bar'] }} etc. """ def __init__(self, node, arg, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.node = node self.arg = arg def get_items(self): return [self.node, self.arg] def __repr__(self): return 'SubscriptExpression(%r, %r)' % ( self.node, self.arg ) class SliceExpression(Expression): """ 1:2:3 etc. """ def __init__(self, start, stop, step, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.start = start self.stop = stop self.step = step def get_items(self): return [self.start, self.stop, self.step] def __repr__(self): return 'SliceExpression(%r, %r, %r)' % ( self.start, self.stop, self.step ) class TupleExpression(Expression): """ For loop unpacking and some other things like multiple arguments for subscripts. """ def __init__(self, items, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.items = items def get_items(self): return list(self.items) def allows_assignments(self): for item in self.items: if not item.allows_assignments(): return False return True def __repr__(self): return 'TupleExpression(%r)' % (self.items,) class ConcatExpression(Expression): """ For {{ foo ~ bar }}. Because of various reasons (especially because unicode conversion takes place for the left and right expression and is better optimized that way) """ def __init__(self, args, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.args = args def get_items(self): return list(self.args) def __repr__(self): return 'ConcatExpression(%r)' % (self.items,) class CompareExpression(Expression): """ {{ foo == bar }}, {{ foo >= bar }} etc. """ def __init__(self, expr, ops, lineno=None, filename=None): Expression.__init__(self, lineno, filename) self.expr = expr self.ops = ops def get_items(self): return [self.expr] + list(chain(*self.ops)) def __repr__(self): return 'CompareExpression(%r, %r)' % ( self.expr, self.ops ) class MulExpression(BinaryExpression): """ {{ foo * bar }} """ class DivExpression(BinaryExpression): """ {{ foo / bar }} """ class FloorDivExpression(BinaryExpression): """ {{ foo // bar }} """ class AddExpression(BinaryExpression): """ {{ foo + bar }} """ class SubExpression(BinaryExpression): """ {{ foo - bar }} """ class ModExpression(BinaryExpression): """ {{ foo % bar }} """ class PowExpression(BinaryExpression): """ {{ foo ** bar }} """ class AndExpression(BinaryExpression): """ {{ foo and bar }} """ class OrExpression(BinaryExpression): """ {{ foo or bar }} """ class NotExpression(UnaryExpression): """ {{ not foo }} """ class NegExpression(UnaryExpression): """ {{ -foo }} """ class PosExpression(UnaryExpression): """ {{ +foo }} """