Index: third_party/jinja2/compiler.py |
diff --git a/third_party/jinja2/compiler.py b/third_party/jinja2/compiler.py |
index 75a60b8d2d6b326d6248b64a0b74538316d4f82c..fad007b596f66fc090d155bb97a8fc5967749608 100644 |
--- a/third_party/jinja2/compiler.py |
+++ b/third_party/jinja2/compiler.py |
@@ -16,7 +16,7 @@ from jinja2.nodes import EvalContext |
from jinja2.visitor import NodeVisitor |
from jinja2.exceptions import TemplateAssertionError |
from jinja2.utils import Markup, concat, escape |
-from jinja2._compat import range_type, next, text_type, string_types, \ |
+from jinja2._compat import range_type, text_type, string_types, \ |
iteritems, NativeStringIO, imap |
@@ -57,7 +57,8 @@ def generate(node, environment, name, filename, stream=None, |
"""Generate the python source for a node tree.""" |
if not isinstance(node, nodes.Template): |
raise TypeError('Can\'t compile non template nodes') |
- generator = CodeGenerator(environment, name, filename, stream, defer_init) |
+ generator = environment.code_generator_class(environment, name, filename, |
+ stream, defer_init) |
generator.visit(node) |
if stream is None: |
return generator.stream.getvalue() |
@@ -347,6 +348,9 @@ class FrameIdentifierVisitor(NodeVisitor): |
def visit_FilterBlock(self, node): |
self.visit(node.filter) |
+ def visit_AssignBlock(self, node): |
+ """Stop visiting at block assigns.""" |
+ |
def visit_Scope(self, node): |
"""Stop visiting at scopes.""" |
@@ -1215,8 +1219,17 @@ class CodeGenerator(NodeVisitor): |
if self.has_known_extends and frame.require_output_check: |
return |
+ allow_constant_finalize = True |
if self.environment.finalize: |
- finalize = lambda x: text_type(self.environment.finalize(x)) |
+ func = self.environment.finalize |
+ if getattr(func, 'contextfunction', False) or \ |
+ getattr(func, 'evalcontextfunction', False): |
+ allow_constant_finalize = False |
+ elif getattr(func, 'environmentfunction', False): |
+ finalize = lambda x: text_type( |
+ self.environment.finalize(self.environment, x)) |
+ else: |
+ finalize = lambda x: text_type(self.environment.finalize(x)) |
else: |
finalize = text_type |
@@ -1232,6 +1245,8 @@ class CodeGenerator(NodeVisitor): |
body = [] |
for child in node.nodes: |
try: |
+ if not allow_constant_finalize: |
+ raise nodes.Impossible() |
const = child.as_const(frame.eval_ctx) |
except nodes.Impossible: |
body.append(child) |
@@ -1287,6 +1302,9 @@ class CodeGenerator(NodeVisitor): |
self.write('to_string(') |
if self.environment.finalize is not None: |
self.write('environment.finalize(') |
+ if getattr(self.environment.finalize, |
+ "contextfunction", False): |
+ self.write('context, ') |
close += 1 |
self.visit(item, frame) |
self.write(')' * close) |
@@ -1309,7 +1327,6 @@ class CodeGenerator(NodeVisitor): |
arguments.append(item) |
self.writeline('yield ') |
self.write(repr(concat(format)) + ' % (') |
- idx = -1 |
self.indent() |
for argument in arguments: |
self.newline(argument) |
@@ -1323,6 +1340,15 @@ class CodeGenerator(NodeVisitor): |
close += 1 |
if self.environment.finalize is not None: |
self.write('environment.finalize(') |
+ if getattr(self.environment.finalize, |
+ 'contextfunction', False): |
+ self.write('context, ') |
+ elif getattr(self.environment.finalize, |
+ 'evalcontextfunction', False): |
+ self.write('context.eval_ctx, ') |
+ elif getattr(self.environment.finalize, |
+ 'environmentfunction', False): |
+ self.write('environment, ') |
close += 1 |
self.visit(argument, frame) |
self.write(')' * close + ', ') |
@@ -1332,42 +1358,62 @@ class CodeGenerator(NodeVisitor): |
if outdent_later: |
self.outdent() |
- def visit_Assign(self, node, frame): |
- self.newline(node) |
+ def make_assignment_frame(self, frame): |
# toplevel assignments however go into the local namespace and |
# the current template's context. We create a copy of the frame |
# here and add a set so that the Name visitor can add the assigned |
# names here. |
- if frame.toplevel: |
- assignment_frame = frame.copy() |
- assignment_frame.toplevel_assignments = set() |
+ if not frame.toplevel: |
+ return frame |
+ assignment_frame = frame.copy() |
+ assignment_frame.toplevel_assignments = set() |
+ return assignment_frame |
+ |
+ def export_assigned_vars(self, frame, assignment_frame): |
+ if not frame.toplevel: |
+ return |
+ public_names = [x for x in assignment_frame.toplevel_assignments |
+ if not x.startswith('_')] |
+ if len(assignment_frame.toplevel_assignments) == 1: |
+ name = next(iter(assignment_frame.toplevel_assignments)) |
+ self.writeline('context.vars[%r] = l_%s' % (name, name)) |
else: |
- assignment_frame = frame |
+ self.writeline('context.vars.update({') |
+ for idx, name in enumerate(assignment_frame.toplevel_assignments): |
+ if idx: |
+ self.write(', ') |
+ self.write('%r: l_%s' % (name, name)) |
+ self.write('})') |
+ if public_names: |
+ if len(public_names) == 1: |
+ self.writeline('context.exported_vars.add(%r)' % |
+ public_names[0]) |
+ else: |
+ self.writeline('context.exported_vars.update((%s))' % |
+ ', '.join(imap(repr, public_names))) |
+ |
+ def visit_Assign(self, node, frame): |
+ self.newline(node) |
+ assignment_frame = self.make_assignment_frame(frame) |
self.visit(node.target, assignment_frame) |
self.write(' = ') |
self.visit(node.node, frame) |
- |
- # make sure toplevel assignments are added to the context. |
- if frame.toplevel: |
- public_names = [x for x in assignment_frame.toplevel_assignments |
- if not x.startswith('_')] |
- if len(assignment_frame.toplevel_assignments) == 1: |
- name = next(iter(assignment_frame.toplevel_assignments)) |
- self.writeline('context.vars[%r] = l_%s' % (name, name)) |
- else: |
- self.writeline('context.vars.update({') |
- for idx, name in enumerate(assignment_frame.toplevel_assignments): |
- if idx: |
- self.write(', ') |
- self.write('%r: l_%s' % (name, name)) |
- self.write('})') |
- if public_names: |
- if len(public_names) == 1: |
- self.writeline('context.exported_vars.add(%r)' % |
- public_names[0]) |
- else: |
- self.writeline('context.exported_vars.update((%s))' % |
- ', '.join(imap(repr, public_names))) |
+ self.export_assigned_vars(frame, assignment_frame) |
+ |
+ def visit_AssignBlock(self, node, frame): |
+ block_frame = frame.inner() |
+ block_frame.inspect(node.body) |
+ aliases = self.push_scope(block_frame) |
+ self.pull_locals(block_frame) |
+ self.buffer(block_frame) |
+ self.blockvisit(node.body, block_frame) |
+ self.pop_scope(aliases, block_frame) |
+ |
+ assignment_frame = self.make_assignment_frame(frame) |
+ self.newline(node) |
+ self.visit(node.target, assignment_frame) |
+ self.write(' = concat(%s)' % block_frame.buffer) |
+ self.export_assigned_vars(frame, assignment_frame) |
# -- Expression Visitors |