[svn] fixes jinja inheritance code
authorArmin Ronacher <armin.ronacher@active-4.com>
Sun, 11 Mar 2007 19:48:08 +0000 (20:48 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sun, 11 Mar 2007 19:48:08 +0000 (20:48 +0100)
--HG--
branch : trunk

jinja/environment.py
jinja/nodes.py
jinja/parser.py
jinja/translators/python.py
tests/templates/a.html
tests/templates/b.html
tests/templates/c.html

index 9943afc1317ab06fb3a5a7747c7d415c4f3b1cbe..78862b80a53e254a32fd08666eb371bc08653a19 100644 (file)
@@ -142,12 +142,12 @@ class Environment(object):
             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):
index fbcd9ed3269d3bea8d2ef3b317449d0ecefe2d89..da8a7401e3a7ecf8a3a95ba8c803642c0fcbe032 100644 (file)
@@ -26,6 +26,18 @@ def inc_lineno(offset, tree):
         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.
@@ -69,10 +81,7 @@ class NodeList(list, 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):
@@ -80,19 +89,24 @@ 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):
index 2b0f58122afc1bad67ccdb63f5f516a71b58bfbc..c5d4e1bea354a28888458d6b883069827c1d08f4 100644 (file)
@@ -14,6 +14,10 @@ from compiler.misc import set_filename
 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
@@ -64,7 +68,7 @@ class Parser(object):
         self.tokenstream = environment.lexer.tokenize(source)
 
         self.extends = None
-        self.blocks = {}
+        self.blocks = set()
 
         self.directives = {
             'for':          self.handle_for_directive,
@@ -221,19 +225,19 @@ class Parser(object):
             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):
         """
@@ -395,7 +399,7 @@ class Parser(object):
         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):
         """
index 7553209d0f2df40ef4030ee7315b3ad5dd0f90d1..55332e12d202b4f2cf3417c82ba3d79cb15b53dc 100644 (file)
@@ -10,6 +10,7 @@
 """
 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
@@ -212,12 +213,13 @@ class PythonTranslator(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:
@@ -231,17 +233,15 @@ class PythonTranslator(Translator):
             # 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 = []
@@ -267,7 +267,6 @@ class PythonTranslator(Translator):
             '    ctx_push = context.push\n'
             '    ctx_pop = context.pop\n'
         ]
-        self.indention = 1
 
         # we have requirements? add them here.
         if requirements:
@@ -490,7 +489,8 @@ class PythonTranslator(Translator):
         """
         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
             ))
@@ -498,7 +498,8 @@ class PythonTranslator(Translator):
         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
         ))
@@ -626,7 +627,7 @@ class PythonTranslator(Translator):
                 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])
         )
@@ -640,8 +641,9 @@ class PythonTranslator(Translator):
         # 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),
index bf9c270b22a5250efeb5fa10aa2513c0286255d8..7aab68328826cc42b58c42414318b74deadabdc9 100644 (file)
@@ -5,5 +5,8 @@
   nested block from template a.html
   {% block block5 %}
     contents of the nested block from a.html
+    {% block block6 %}
+      double nested block.
+    {% endblock %}
   {% endblock %}
 {% endblock %}
index 181fb2436e375af9a3636bc7b835c3446b315e34..32626f33bfaa1450fd371ce27e1e0e1905afec92 100644 (file)
@@ -1,3 +1,8 @@
 {% 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 %}
index ffb72362c5e1dc34ef1ba6b48d3f5f739d0d1123..1cc4f71061b95336bfd0ced357bce0b2b3419a07 100644 (file)
@@ -1,3 +1,6 @@
 {% 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 %}