]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Added support for unoptimized scopes
authorArmin Ronacher <armin.ronacher@active-4.com>
Sun, 8 Jan 2017 11:01:27 +0000 (12:01 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sun, 8 Jan 2017 11:01:27 +0000 (12:01 +0100)
CHANGES
jinja2/compiler.py
jinja2/idtracking.py
jinja2/nodes.py
jinja2/runtime.py
tests/test_ext.py

diff --git a/CHANGES b/CHANGES
index 8a160d79a55b888464ef13899af0ba4a2f1f1e93..bc038cdb48506a37098b92f27ba8ff7d59ee046b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,14 @@
 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)
index 18cc2ec074aee06f80f254099b85ac20be59203f..a9ee446a0e424f96bff483d1da7b6b3d31667215 100644 (file)
@@ -130,9 +130,10 @@ class MacroRef(object):
 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
@@ -168,8 +169,10 @@ class Frame(object):
         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):
@@ -1653,7 +1656,7 @@ class CodeGenerator(NodeVisitor):
         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)
index 697d4a20a73cc47eb0d45f7774039637c22c05d5..dc8741cb7ee814725e6cfd3fd39e6e8f09e77ac4 100644 (file)
@@ -24,11 +24,13 @@ def symbols_for_node(node, parent_symbols=None):
 
 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 = {}
index a7641a4895360b3b357cc45e850426c97da00336..5afec8798796e93385a74f4b1ed8e57a382bab3e 100644 (file)
@@ -907,8 +907,16 @@ class Scope(Stmt):
 
 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')
 
index deb1c89794c756cc5c2ff9a2225e3d2207d48adf..5c2089b8c0a6206a8ed0465cd418e3008b32c748 100644 (file)
@@ -24,7 +24,7 @@ from jinja2._compat import imap, text_type, iteritems, \
 __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.
index 79f783066ea8886213f9ae6e8340d6928f1aa9a0..c85435c27135879701edbcba82c2eb1aa3b391c5 100644 (file)
@@ -473,3 +473,26 @@ class TestAutoEscape(object):
                           autoescape=True)
         pysource = env.compile(tmplsource, raw=True)
         assert '&lt;testing&gt;\\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'