two similarly-named ``{% block %}`` tags in a template, that template's
parent wouldn't know which one of the blocks' content to use.
+How Inheritance Works Internally
+--------------------------------
+
+Inheritance in Jinja is straighforward. If a template contains a
+``{% extends %}`` tag it's considered being a child template, otherwise it's
+a layout template. In a layout template you can place blocks basically
+everywhere. In a child template blocks can only be located either top level
+or inside of another block.
+
+Data outside of a block in a child template is executed before the layout
+template is rendered, thus you can use it to propagate data to the whole
+inheritance chain. Having a block in an invalid position you will receive
+an syntax error. Here some examples:
+
+**impossible**:
+
+ .. sourcecode:: jinja
+
+ {% extends 'master.html' %}
+ {% if some_condition %}
+ {% block body %}
+ ...
+ {% endblock %}
+ {% endif %}
+
+ This can't work because template inheritance works at translation /
+ compilation time not at template executation.
+
+**possible**:
+
+ .. sourcecode:: jinja
+
+ {% extends 'master.html' %}
+ {% block body %}
+ {% if some_condition %}
+ {% block myblock %}
+ ...
+ {% endblock %}
+ {% endblock %}
+ {% endblock %}
+
+ This can work although it probably makes no sense in this specific case.
+ However the condition is handled at runtime because it's in a valid block
+ and defines a new block subtemplates can override.
Super Blocks
============
filename attributes for all nodes in the tree.
"""
body = self.subparse(None)
- todo = [body]
- while todo:
- node = todo.pop()
- # all names excluding keywords have an trailing underline.
- # if we find a name without trailing underline that's a keyword
- # and this code raises an error. else strip the underline again
- if node.__class__ in (ast.AssName, ast.Name, ast.Keyword):
- if not node.name.endswith('_'):
- raise TemplateSyntaxError('illegal use of keyword %r '
- 'as identifier.' % node.name,
- node.lineno, self.filename)
- node.name = node.name[:-1]
- # same for attributes
- elif node.__class__ is ast.Getattr:
- if not node.attrname.endswith('_'):
- raise TemplateSyntaxError('illegal use of keyword %r '
- 'as attribute name.' %
- node.name, node.lineno,
- self.filename)
- node.attrname = node.attrname[:-1]
- # if we have a ForLoop we ensure that nobody patches existing
- # object using "for foo.bar in seq"
- elif node.__class__ is nodes.ForLoop:
- def check(node):
- if node.__class__ not in (ast.AssName, ast.AssTuple):
- raise TemplateSyntaxError('can\'t assign to '
- 'expression.', node.lineno,
+ def walk(nodes_, stack):
+ for node in nodes_:
+ # all names excluding keywords have an trailing underline.
+ # if we find a name without trailing underline that's a
+ # keyword and this code raises an error. else strip the
+ # underline again
+ if node.__class__ in (ast.AssName, ast.Name, ast.Keyword):
+ if not node.name.endswith('_'):
+ raise TemplateSyntaxError('illegal use of keyword %r '
+ 'as identifier.' %
+ node.name, node.lineno,
+ self.filename)
+ node.name = node.name[:-1]
+ # same for attributes
+ elif node.__class__ is ast.Getattr:
+ if not node.attrname.endswith('_'):
+ raise TemplateSyntaxError('illegal use of keyword %r '
+ 'as attribute name.' %
+ node.name, node.lineno,
self.filename)
- for n in node.getChildNodes():
- check(n)
- check(node.item)
- # now set the filename and continue working on the childnodes
- node.filename = self.filename
- todo.extend(node.getChildNodes())
+ node.attrname = node.attrname[:-1]
+ # if we have a ForLoop we ensure that nobody patches existing
+ # object using "for foo.bar in seq"
+ elif node.__class__ is nodes.ForLoop:
+ def check(node):
+ if node.__class__ not in (ast.AssName, ast.AssTuple):
+ raise TemplateSyntaxError('can\'t assign to '
+ 'expression.',
+ node.lineno,
+ self.filename)
+ for n in node.getChildNodes():
+ check(n)
+ check(node.item)
+ # ensure that in child templates block are either top level
+ # or located inside of another block tag.
+ elif self.extends is not None and \
+ node.__class__ is nodes.Block:
+ if stack[-1] is not body:
+ for n in stack:
+ if n.__class__ is nodes.Block:
+ break
+ else:
+ raise TemplateSyntaxError('misplaced block %r, '
+ 'blocks in child '
+ 'templates must be '
+ 'either top level or '
+ 'located in a block '
+ 'tag.' % node.name,
+ node.lineno,
+ self.filename)
+ # now set the filename and continue working on the childnodes
+ node.filename = self.filename
+ stack.append(node)
+ walk(node.getChildNodes(), stack)
+ stack.pop()
+ walk([body], [body])
return nodes.Template(self.filename, body, self.extends)
def subparse(self, test, drop_needle=False):