- Fixed a bug that caused syntax errors when defining macros or using the
`{% call %}` tag inside loops.
- Fixed a bug in the parser that made ``{{ foo[1, 2] }}`` impossible.
+- Made it possible to refer to names from outer scopes in included templates
+ that were unused in the callers frame (#327)
Version 2.1.1
-------------
self.writeline('%s = environment.%s[%r]' %
(mapping[name], dependency, name))
+ def unoptimize_scope(self, frame):
+ """Disable Python optimizations for the frame."""
+ # XXX: this is not that nice but it has no real overhead. It
+ # mainly works because python finds the locals before dead code
+ # is removed. If that breaks we have to add a dummy function
+ # that just accepts the arguments and does nothing.
+ if frame.identifiers.declared:
+ self.writeline('if 0: dummy(%s)' % ', '.join(
+ 'l_' + name for name in frame.identifiers.declared))
+
def push_scope(self, frame, extra_vars=()):
"""This function returns all the shadowed variables in a dict
in the form name: alias and will write the required assignments
def visit_Include(self, node, frame):
"""Handles includes."""
+ if node.with_context:
+ self.unoptimize_scope(frame)
if node.ignore_missing:
self.writeline('try:')
self.indent()
def visit_Import(self, node, frame):
"""Visit regular imports."""
+ if node.with_context:
+ self.unoptimize_scope(frame)
self.writeline('l_%s = ' % node.target, node)
if frame.toplevel:
self.write('context.vars[%r] = ' % node.target)
test_env = Environment(loader=DictLoader(dict(
module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}',
- header='[{{ foo }}|{{ 23 }}]'
+ header='[{{ foo }}|{{ 23 }}]',
+ o_printer='({{ o }})'
)))
test_env.globals['bar'] = 23
assert not hasattr(m, '__missing')
assert m.variable == 42
assert not hasattr(m, 'notthere')
+
+
+def test_unoptimized_scopes():
+ t = test_env.from_string("""
+ {% macro outer(o) %}
+ {% macro inner() %}
+ {% include "o_printer" %}
+ {% endmacro %}
+ {{ inner() }}
+ {% endmacro %}
+ {{ outer("FOO") }}
+ """)
+ assert t.render().strip() == '(FOO)'