try:
node = node[name]
except (TypeError, KeyError, IndexError):
- if not hasattr(obj, name):
+ if not hasattr(node, name):
return Undefined
r = getattr(obj, 'jinja_allowed_attributes', None)
if r is not None and name not in r:
raise SecurityException('unsafe attributed %r accessed' % name)
- node = getattr(obj, name)
+ node = getattr(node, name)
return node
def call_function(self, f, args, kwargs, dyn_args, dyn_kwargs):
todo.extend(node.getChildNodes())
+def get_nodes(nodetype, tree):
+ """
+ Get all nodes from nodetype in the tree.
+ """
+ todo = [tree]
+ while todo:
+ node = todo.pop()
+ if node.__class__ is nodetype:
+ yield node
+ todo.extend(node.getChildNodes())
+
+
class Node(ast.Node):
"""
jinja node.
getChildren = getChildNodes = lambda s: list(s) + s.get_items()
def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- list.__repr__(self)
- )
+ return 'NodeList(%s)' % list.__repr__(self)
class Template(NodeList):
Node that represents a template.
"""
- def __init__(self, filename, body, blocks, extends):
+ def __init__(self, filename, body, extends):
if body.__class__ is not NodeList:
body = (body,)
NodeList.__init__(self, 0, body)
- self.blocks = blocks
self.extends = extends
set_filename(filename, self)
def get_items(self):
- result = self.blocks.values()
if self.extends is not None:
- result.append(self.extends)
- return result
+ return [self.extends]
+ return []
+
+ def __repr__(self):
+ return 'Template(%r, %r, %r)' % (
+ self.filename,
+ self.extends,
+ NodeList.__repr__(self)
+ )
class ForLoop(Node):
from jinja import nodes
from jinja.datastructure import TokenStream
from jinja.exceptions import TemplateSyntaxError
+try:
+ set
+except NameError:
+ from sets import Set as set
# callback functions for the subparse method
self.tokenstream = environment.lexer.tokenize(source)
self.extends = None
- self.blocks = {}
+ self.blocks = set()
self.directives = {
'for': self.handle_for_directive,
raise TemplateSyntaxError('expected \'name\', got %r' %
block_name[1], lineno)
if tokens:
- print tokens
raise TemplateSyntaxError('block got too many arguments, '
'requires one.', lineno)
+ # check if this block does not exist by now.
if block_name[2] in self.blocks:
raise TemplateSyntaxError('block %r defined twice' %
- block_name[2], lineno)
+ block_name[2], lineno)
+ self.blocks.add(block_name[2])
+ # now parse the body and attach it to the block
body = self.subparse(end_of_block_tag, True)
self.close_remaining_block()
- rv = nodes.Block(lineno, block_name[2], body)
- self.blocks[block_name[2]] = rv
- return rv
+ return nodes.Block(lineno, block_name[2], body)
def handle_extends_directive(self, lineno, gen):
"""
Parse the template and return a Template node.
"""
body = self.subparse(None)
- return nodes.Template(self.filename, body, self.blocks, self.extends)
+ return nodes.Template(self.filename, body, self.extends)
def subparse(self, test, drop_needle=False):
"""
"""
from compiler import ast
from jinja import nodes
+from jinja.nodes import get_nodes
from jinja.parser import Parser
from jinja.exceptions import TemplateSyntaxError
from jinja.translators import Translator
inheritance information. It only occours as outer node, never in
the tree itself.
"""
+ self.indention = 1
+
# if there is a parent template we parse the parent template and
# update the blocks there. Once this is done we drop the current
# template in favor of the new one. Do that until we found the
# root template.
requirements_todo = []
- blocks = node.blocks.copy()
parent = None
while node.extends is not None:
# to the block registry, make this template the new root.
parent = self.environment.loader.parse(node.extends.template,
node.filename)
- for name, block in parent.blocks.iteritems():
- if name not in blocks:
- blocks[name] = block
- node = parent
+ overwrites = {}
+ for n in get_nodes(nodes.Block, node):
+ overwrites[n.name] = n
+ for n in get_nodes(nodes.Block, parent):
+ if n.name in overwrites:
+ n.replace(overwrites[n.name])
- # if there is a parent template, do the inheritance handling now
- if parent is not None:
- for name, block in blocks.iteritems():
- if name in node.blocks:
- node.blocks[name].replace(block)
+ node = parent
# look up requirements
requirements = []
' ctx_push = context.push\n'
' ctx_pop = context.pop\n'
]
- self.indention = 1
# we have requirements? add them here.
if requirements:
"""
rv = self.handle_node(node.body)
if not rv:
- return self.indent('# EMPTY BLOCK FROM %r, LINE %s' % (
+ return self.indent('# EMPTY BLOCK "%s" FROM %r, LINE %s' % (
+ node.name,
node.filename or '?',
node.lineno
))
buf = []
write = lambda x: buf.append(self.indent(x))
- write('# BLOCK FROM %r, LINE %s' % (
+ write('# BLOCK "%s" FROM %r, LINE %s' % (
+ node.name,
node.filename or '?',
node.lineno
))
self.handle_node(node.expr),
self.handle_node(node.subs[0])
)
- return 'get_attribute(%s, %s)' % (
+ return 'get_attribute(%s, (%s,))' % (
self.handle_node(node.expr),
self.handle_node(node.subs[0])
)
# chain getattrs for speed reasons
path = [repr(node.attrname)]
while node.expr.__class__ is ast.Getattr:
- path.append(repr(node.attrname))
node = node.expr
+ path.append(repr(node.attrname))
+ path.reverse()
return 'get_attribute(%s, %s)' % (
self.handle_node(node.expr),
nested block from template a.html
{% block block5 %}
contents of the nested block from a.html
+ {% block block6 %}
+ double nested block.
+ {% endblock %}
{% endblock %}
{% endblock %}
{% extends 'a.html' %}
{% block block1 %}from template b.html{% endblock %}
-{% block block5 %}contents of nested block from b.html{% endblock %}
+{% block block5 %}
+ contents of nested block from b.html
+ {% block block7 %}
+ new nested block introduced in b.html
+ {% endblock %}
+{% endblock %}
{% extends 'b.html' %}
{% block block2 %}from template c.html{% endblock %}
{% block block3 %}from template c.html{% endblock %}
+{% block block7 %}
+ nested block from b.html, overridden in c.html
+{% endblock %}