diff options
author | Armin Ronacher <armin.ronacher@active-4.com> | 2017-01-08 11:10:55 +0100 |
---|---|---|
committer | Armin Ronacher <armin.ronacher@active-4.com> | 2017-01-08 11:10:55 +0100 |
commit | 9aa267e08a1b2ce7a0d1f9737fe2614173cf4f8b (patch) | |
tree | b94063d1ea92a8d545f80f00e385dafbf64ce10f | |
parent | 62d72eea41528602b88d162377e1cbd8f24f0cc4 (diff) | |
download | jinja2-feature/overlay-scopes.tar.gz |
WIP for overlay scopesfeature/overlay-scopes
-rw-r--r-- | jinja2/compiler.py | 49 | ||||
-rw-r--r-- | jinja2/idtracking.py | 7 | ||||
-rw-r--r-- | jinja2/nodes.py | 8 | ||||
-rw-r--r-- | jinja2/runtime.py | 2 |
4 files changed, 61 insertions, 5 deletions
diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 9a6ac6a..18cc2ec 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -302,6 +302,9 @@ class CodeGenerator(NodeVisitor): # Tracks parameter definition blocks self._param_def_block = [] + # Tracks the current context. + self._context_reference_stack = ['context'] + # -- Various compilation helpers def fail(self, msg, lineno): @@ -473,8 +476,8 @@ class CodeGenerator(NodeVisitor): if action == VAR_LOAD_PARAMETER: pass elif action == VAR_LOAD_RESOLVE: - self.writeline('%s = resolve(%r)' % - (target, param)) + self.writeline('%s = %s(%r)' % + (target, self.get_resolve_func(), param)) elif action == VAR_LOAD_ALIAS: self.writeline('%s = %s' % (target, param)) elif action == VAR_LOAD_UNDEFINED: @@ -626,6 +629,27 @@ class CodeGenerator(NodeVisitor): if self._param_def_block: self._param_def_block[-1].discard(target) + def push_context_reference(self, target): + self._context_reference_stack.append(target) + + def pop_context_reference(self): + self._context_reference_stack.pop() + + def get_context_ref(self): + return self._context_reference_stack[-1] + + def get_resolve_func(self): + target = self._context_reference_stack[-1] + if target == 'context': + return 'resolve' + return '%s.resolve' % target + + def derive_context(self, frame): + return '%s.derived(%s)' % ( + self.get_context_ref(), + self.dump_local_context(frame), + ) + def parameter_is_undeclared(self, target): """Checks if a given target is an undeclared parameter.""" if not self._param_def_block: @@ -793,8 +817,11 @@ class CodeGenerator(NodeVisitor): self.writeline('if parent_template is None:') self.indent() level += 1 - context = node.scoped and ( - 'context.derived(%s)' % self.dump_local_context(frame)) or 'context' + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() if supports_yield_from and not self.environment.is_async and \ frame.buffer is None: @@ -1619,6 +1646,20 @@ class CodeGenerator(NodeVisitor): self.blockvisit(node.body, scope_frame) self.leave_frame(scope_frame) + def visit_OverlayScope(self, node, frame): + ctx = self.temporary_identifier() + self.writeline('%s = %s' % (ctx, self.derive_context(frame))) + self.writeline('%s.vars = ' % ctx) + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = Frame(frame.eval_ctx) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + def visit_EvalContextModifier(self, node, frame): for keyword in node.options: self.writeline('context.eval_ctx.%s = ' % keyword.key) diff --git a/jinja2/idtracking.py b/jinja2/idtracking.py index b00dab8..697d4a2 100644 --- a/jinja2/idtracking.py +++ b/jinja2/idtracking.py @@ -167,6 +167,10 @@ class RootVisitor(NodeVisitor): for child in node.iter_child_nodes(exclude=('call',)): self.sym_visitor.visit(child) + def visit_OverlayScope(self, node, **kwargs): + for child in node.body: + self.sym_visitor.visit(child) + def visit_For(self, node, for_branch='body', **kwargs): if node.test is not None: self.sym_visitor.visit(node.test) @@ -258,3 +262,6 @@ class FrameSymbolVisitor(NodeVisitor): def visit_Block(self, node, **kwargs): """Stop visiting at blocks.""" + + def visit_OverlayScope(self, node, **kwargs): + """Do not visit into overlay scopes.""" diff --git a/jinja2/nodes.py b/jinja2/nodes.py index d1a4c38..a7641a4 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -905,6 +905,14 @@ class Scope(Stmt): fields = ('body',) +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. + """ + fields = ('context', 'body') + + class EvalContextModifier(Stmt): """Modifies the eval context. For each option that should be modified, a :class:`Keyword` has to be added to the :attr:`options` list. diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 958ddfd..4f0f399 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', - 'TemplateNotFound'] + 'make_overlay_resolve', 'TemplateNotFound'] #: the name of the function that is used to convert something into #: a string. We can just use the text type here. |