From: Armin Ronacher Date: Sun, 8 Jan 2017 11:01:27 +0000 (+0100) Subject: Added support for unoptimized scopes X-Git-Tag: 2.10~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6173cf6fc2a86f7093f4b6911b0fb3ceb30db152;p=thirdparty%2Fjinja.git Added support for unoptimized scopes --- diff --git a/CHANGES b/CHANGES index 8a160d79..bc038cdb 100644 --- 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) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 18cc2ec0..a9ee446a 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -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) diff --git a/jinja2/idtracking.py b/jinja2/idtracking.py index 697d4a20..dc8741cb 100644 --- a/jinja2/idtracking.py +++ b/jinja2/idtracking.py @@ -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 = {} diff --git a/jinja2/nodes.py b/jinja2/nodes.py index a7641a48..5afec879 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -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') diff --git a/jinja2/runtime.py b/jinja2/runtime.py index deb1c897..5c2089b8 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -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. diff --git a/tests/test_ext.py b/tests/test_ext.py index 79f78306..c85435c2 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -473,3 +473,26 @@ class TestAutoEscape(object): 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'