From 4f41711f483eb745ed3d81b6d0dca31725db79ad Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 28 Apr 2007 23:23:44 +0200 Subject: [PATCH] [svn] added a sanity check for block tags outside the root level --HG-- branch : trunk --- CHANGES | 2 + docs/src/designerdoc.txt | 44 ++++++++++++++++++++ docs/src/i18n.txt | 4 ++ jinja/parser.py | 88 +++++++++++++++++++++++++--------------- 4 files changed, 105 insertions(+), 33 deletions(-) diff --git a/CHANGES b/CHANGES index be02762..8dcac27 100644 --- a/CHANGES +++ b/CHANGES @@ -81,6 +81,8 @@ Version 1.1 - it's not possible to stream templates. +- fixed a corner case when defining a block inside of a condition + Version 1.0 ----------- diff --git a/docs/src/designerdoc.txt b/docs/src/designerdoc.txt index 7179292..6c6dbab 100644 --- a/docs/src/designerdoc.txt +++ b/docs/src/designerdoc.txt @@ -611,6 +611,50 @@ value from the parent template is used instead. 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 ============ diff --git a/docs/src/i18n.txt b/docs/src/i18n.txt index 621cc91..cfad370 100644 --- a/docs/src/i18n.txt +++ b/docs/src/i18n.txt @@ -50,6 +50,10 @@ However. For many web applications this might be a way: tmpl = env.get_template('index.html') tmpl.render(LANGUAGE='de_DE') +This example assumes that you use gettext and have a gettext +`Translations` object which is returned by the `get_translator` +function. + Collecting Translations ======================= diff --git a/jinja/parser.py b/jinja/parser.py index a7e2e05..207dd54 100644 --- a/jinja/parser.py +++ b/jinja/parser.py @@ -574,40 +574,62 @@ class Parser(object): 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): -- 2.26.2