oohEmbed/app/jinja/nodes.py
2009-07-18 11:32:16 +08:00

792 lines
17 KiB
Python

# -*- 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 }}
"""