From: Armin Ronacher Date: Sun, 11 Mar 2007 19:48:08 +0000 (+0100) Subject: [svn] fixes jinja inheritance code X-Git-Tag: 2.0rc1~451 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=56ae6e06a79ecc86ff7d638d10a37b0b69a139c6;p=jinja2.git [svn] fixes jinja inheritance code --HG-- branch : trunk --- diff --git a/jinja/environment.py b/jinja/environment.py index 9943afc..78862b8 100644 --- a/jinja/environment.py +++ b/jinja/environment.py @@ -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): diff --git a/jinja/nodes.py b/jinja/nodes.py index fbcd9ed..da8a740 100644 --- a/jinja/nodes.py +++ b/jinja/nodes.py @@ -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): diff --git a/jinja/parser.py b/jinja/parser.py index 2b0f581..c5d4e1b 100644 --- a/jinja/parser.py +++ b/jinja/parser.py @@ -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): """ diff --git a/jinja/translators/python.py b/jinja/translators/python.py index 7553209..55332e1 100644 --- a/jinja/translators/python.py +++ b/jinja/translators/python.py @@ -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), diff --git a/tests/templates/a.html b/tests/templates/a.html index bf9c270..7aab683 100644 --- a/tests/templates/a.html +++ b/tests/templates/a.html @@ -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 %} diff --git a/tests/templates/b.html b/tests/templates/b.html index 181fb24..32626f3 100644 --- a/tests/templates/b.html +++ b/tests/templates/b.html @@ -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 %} diff --git a/tests/templates/c.html b/tests/templates/c.html index ffb7236..1cc4f71 100644 --- a/tests/templates/c.html +++ b/tests/templates/c.html @@ -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 %}