Jinja2 Changelog
================
+Version 2.10
+------------
+(feature release, release date to be decided)
+
+- Added a new extension node called `OverlayScope` which can be used to
+ create an unoptimized scope that will look up all variables from a
+ derived context.
+
Version 2.9.3
-------------
(bugfix release, release date to be decided)
class Frame(object):
"""Holds compile time information for us."""
- def __init__(self, eval_ctx, parent=None):
+ def __init__(self, eval_ctx, parent=None, level=None):
self.eval_ctx = eval_ctx
- self.symbols = Symbols(parent and parent.symbols or None)
+ self.symbols = Symbols(parent and parent.symbols or None,
+ level=level)
# a toplevel frame is the root + soft frames such as if conditions.
self.toplevel = False
rv.symbols = self.symbols.copy()
return rv
- def inner(self):
+ def inner(self, isolated=False):
"""Return an inner frame."""
+ if isolated:
+ return Frame(self.eval_ctx, level=self.symbols.level + 1)
return Frame(self.eval_ctx, self)
def soft(self):
self.visit(node.context, frame)
self.push_context_reference(ctx)
- scope_frame = Frame(frame.eval_ctx)
+ scope_frame = frame.inner(isolated=True)
scope_frame.symbols.analyze_node(node)
self.enter_frame(scope_frame)
self.blockvisit(node.body, scope_frame)
class Symbols(object):
- def __init__(self, parent=None):
- if parent is None:
- self.level = 0
- else:
- self.level = parent.level + 1
+ def __init__(self, parent=None, level=None):
+ if level is None:
+ if parent is None:
+ level = 0
+ else:
+ level = parent.level + 1
+ self.level = level
self.parent = parent
self.refs = {}
self.loads = {}
class OverlayScope(Stmt):
"""An overlay scope for extensions. This is a largely unoptimized scope
- that however can be used to introduce completely arbitrar variables into
- a sub scope from a dictionary or dictionary like object.
+ that however can be used to introduce completely arbitrary variables into
+ a sub scope from a dictionary or dictionary like object. The `context`
+ field has to evaluate to a dictionary object.
+
+ Example usage::
+
+ OverlayScope(context=self.call_method('get_context'),
+ body=[...])
+
+ .. versionadded:: 2.10
"""
fields = ('context', 'body')
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
'TemplateRuntimeError', 'missing', 'concat', 'escape',
'markup_join', 'unicode_join', 'to_string', 'identity',
- 'make_overlay_resolve', 'TemplateNotFound']
+ 'TemplateNotFound']
#: the name of the function that is used to convert something into
#: a string. We can just use the text type here.
autoescape=True)
pysource = env.compile(tmplsource, raw=True)
assert '<testing>\\n' in pysource
+
+ def test_overlay_scopes(self):
+ class MagicScopeExtension(Extension):
+ tags = set(['overlay'])
+ def parse(self, parser):
+ node = nodes.OverlayScope(lineno=next(parser.stream).lineno)
+ node.body = list(parser.parse_statements(('name:endoverlay',),
+ drop_needle=True))
+ node.context = self.call_method('get_scope')
+ return node
+ def get_scope(self):
+ return {'x': [1, 2, 3]}
+
+ env = Environment(extensions=[MagicScopeExtension])
+
+ tmpl = env.from_string('''
+ {{- x }}|{% set z = 99 %}
+ {%- overlay %}
+ {{- y }}|{{ z }}|{% for item in x %}[{{ item }}]{% endfor %}
+ {%- endoverlay %}|
+ {{- x -}}
+ ''')
+ assert tmpl.render(x=42, y=23) == '42|23|99|[1][2][3]|42'