[svn] implemented `{{ super() }}` for blocks. This checkin makes jinja much slower. I'll improve that as soon as possible
--HG--
branch : trunk
diff --git a/jdebug.py b/jdebug.py
index 5de8815..109c5c6 100644
--- a/jdebug.py
+++ b/jdebug.py
@@ -8,6 +8,8 @@
:copyright: 2006 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
+import os
+import sys
from jinja import Environment
from jinja.parser import Parser
from jinja.lexer import Lexer
@@ -19,6 +21,18 @@
e = Environment()
t = e.from_string
+
+if os.environ.get('JDEBUG_SOURCEPRINT'):
+ original_translate = PythonTranslator.translate
+
+ def debug_translate(self):
+ rv = original_translate(self)
+ sys.stderr.write('## GENERATED SOURCE:\n%s\n' % rv)
+ return rv
+
+ PythonTranslator.translate = debug_translate
+
+
def p(x):
print PythonTranslator(e, Parser(e, x).parse()).translate()
diff --git a/jinja/datastructure.py b/jinja/datastructure.py
index 3b8448e..e64f687 100644
--- a/jinja/datastructure.py
+++ b/jinja/datastructure.py
@@ -316,6 +316,26 @@
return seq[self.lineno]
+class BlockContext(object):
+ """
+ Helper class for ``{{ super() }}``.
+ """
+ jinja_allowed_attributes = ['name']
+
+ def __init__(self, name, stack, level, context):
+ self.name = name
+ self.context = context
+ if len(stack) > level:
+ self.block = stack[level]
+ else:
+ self.block = None
+
+ def __call__(self):
+ if self.block is None:
+ raise TemplateRuntimeError('no super block for %r' % self.name)
+ return self.block(self.context)
+
+
class TokenStream(object):
"""
A token stream works like a normal generator just that
diff --git a/jinja/nodes.py b/jinja/nodes.py
index b19994e..b9d3051 100644
--- a/jinja/nodes.py
+++ b/jinja/nodes.py
@@ -272,6 +272,14 @@
assert node.__class__ is Block
self.__dict__.update(node.__dict__)
+ def clone(self):
+ """
+ Create an independent clone of this node.
+ """
+ rv = Block(None, None, None)
+ rv.__dict__.update(self.__dict__)
+ return rv
+
def get_items(self):
return [self.name, self.body]
diff --git a/jinja/translators/python.py b/jinja/translators/python.py
index 9608210..e8e9a59 100644
--- a/jinja/translators/python.py
+++ b/jinja/translators/python.py
@@ -168,7 +168,7 @@
"""
return (' ' * (self.indention * 4)) + text
- def nodeinfo(self, node):
+ def nodeinfo(self, node, force=False):
"""
Return a comment that helds the node informations or None
if there is no need to add a debug comment.
@@ -179,7 +179,7 @@
node.filename,
node.lineno
)
- if rv != self.last_debug_comment:
+ if force or rv != self.last_debug_comment:
self.last_debug_comment = rv
return rv
@@ -260,6 +260,7 @@
requirements_todo = []
parent = None
overwrites = {}
+ blocks = {}
while node.extends is not None:
# handle all requirements but not those from the
@@ -271,11 +272,17 @@
# load the template we inherit from and add not known blocks
parent = self.environment.loader.parse(node.extends.template,
node.filename)
- # look up all block nodes and let them override each other
+ # look up all block nodes in the current template and
+ # add them to the override dict
for n in get_nodes(nodes.Block, node):
overwrites[n.name] = n
+ # handle direct overrides
for n in get_nodes(nodes.Block, parent):
+ # an overwritten block for the parent template. handle that
+ # override in the template and register it in the deferred
+ # block dict.
if n.name in overwrites:
+ blocks.setdefault(n.name, []).append(n.clone())
n.replace(overwrites[n.name])
# make the parent node the new node
node = parent
@@ -287,22 +294,26 @@
if n.__class__ in (nodes.Set, nodes.Macro, nodes.Include):
requirements.append(n)
+ # aliases boilerplate
+ aliases = ['%s = environment.%s' % (item, item) for item in
+ ['get_attribute', 'perform_test', 'apply_filters',
+ 'call_function', 'call_function_simple', 'finish_var']]
+
# bootstrapping code
lines = [
'from __future__ import division\n'
- 'from jinja.datastructure import Undefined, LoopContext, CycleContext\n'
+ 'from jinja.datastructure import Undefined, LoopContext, '
+ 'CycleContext, BlockContext\n'
'from jinja.utils import buffereater\n'
+ '%s\n'
'__name__ = %r\n\n'
'def generate(context):\n'
' assert environment is context.environment\n'
- ' get_attribute = environment.get_attribute\n'
- ' perform_test = environment.perform_test\n'
- ' apply_filters = environment.apply_filters\n'
- ' call_function = environment.call_function\n'
- ' call_function_simple = environment.call_function_simple\n'
- ' finish_var = environment.finish_var\n'
' ctx_push = context.push\n'
- ' ctx_pop = context.pop\n' % node.filename
+ ' ctx_pop = context.pop' % (
+ '\n'.join(aliases),
+ node.filename
+ )
]
# we have requirements? add them here.
@@ -323,9 +334,40 @@
' return translator.gettext(s) % (r or {})\n'
' return translator.ngettext(s, p, r[n]) % (r or {})'
)
+
+ # add body lines and "generator hook"
lines.extend(body_lines)
lines.append(' if False:\n yield None')
+ # add the missing blocks
+ if blocks:
+ block_items = blocks.items()
+ block_items.sort()
+ dict_lines = []
+ for name, items in block_items:
+ tmp = []
+ for idx, item in enumerate(items):
+ # ensure that the indention is correct
+ self.indention = 1
+ func_name = 'block_%s_%s' % (name, idx)
+ lines.extend([
+ '\ndef %s(context):' % func_name,
+ ' ctx_push = context.push',
+ ' ctx_pop = context.pop',
+ ' if False:',
+ ' yield None'
+ ])
+ nodeinfo = self.nodeinfo(item, True)
+ if nodeinfo:
+ lines.append(self.indent(nodeinfo))
+ lines.append(self.handle_block(item, idx + 1))
+ tmp.append('buffereater(%s)' % func_name)
+ dict_lines.append(' %r: %s' % (
+ str(name),
+ _to_tuple(tmp)
+ ))
+ lines.append('\nblocks = {\n%s\n}' % ',\n'.join(dict_lines))
+
return '\n'.join(lines)
def handle_template_text(self, node):
@@ -346,7 +388,7 @@
if buf:
nodeinfo = self.nodeinfo(node)
if nodeinfo:
- buf = [self.indent(nodeinfo)] + buf
+ buf.insert(0, self.indent(nodeinfo))
return '\n'.join(buf)
def handle_for_loop(self, node):
@@ -559,7 +601,7 @@
write('yield %s' % self.filter('u\'\'.join(filtered())', node.filters))
return '\n'.join(buf)
- def handle_block(self, node):
+ def handle_block(self, node, level=0):
"""
Handle blocks in the sourcecode. We only use them to
call the current block implementation that is stored somewhere
@@ -572,7 +614,11 @@
buf = []
write = lambda x: buf.append(self.indent(x))
- write('ctx_push()')
+ write('ctx_push({\'super\': BlockContext(%r, blocks[%r], %r, context)})' % (
+ str(node.name),
+ str(node.name),
+ level
+ ))
nodeinfo = self.nodeinfo(node.body)
if nodeinfo:
write(nodeinfo)
diff --git a/tests/runtime/super.py b/tests/runtime/super.py
new file mode 100644
index 0000000..8954afe
--- /dev/null
+++ b/tests/runtime/super.py
@@ -0,0 +1,12 @@
+# test file for block super support
+import jdebug
+from jinja import Environment, DictLoader
+
+env = Environment(loader=DictLoader({
+ 'a': '{% block intro %}INTRO{% endblock %}|BEFORE|{% block data %}INNER{% endblock %}|AFTER',
+ 'b': '{% extends "a" %}{% block data %}({{ super() }}){% endblock %}',
+ 'c': '{% extends "b" %}{% block intro %}--{{ super() }}--{% endblock %}\n{% block data %}[{{ super() }}]{% endblock %}'
+}))
+
+tmpl = env.get_template('c')
+print tmpl.render()