From: Armin Ronacher Date: Mon, 2 Jan 2017 20:12:23 +0000 (+0100) Subject: Fixed various breakage from the new id tracking X-Git-Tag: 2.9~30^2~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=058bc8a98d1c54df0d3523fdb6358608b5a9c950;p=thirdparty%2Fjinja.git Fixed various breakage from the new id tracking --- diff --git a/jinja2/compiler.py b/jinja2/compiler.py index c11de4e6..4aa0293f 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -473,19 +473,17 @@ class CodeGenerator(NodeVisitor): return 'async def %s' % name return 'def %s' % name - def macro_body(self, node, frame, children=None): + def macro_body(self, node, frame): """Dump the function def of a macro or call block.""" - if children is None: - children = list(node.iter_child_nodes()) - children = list(children) frame = frame.inner() + frame.symbols.analyze_node(node) macro_ref = MacroRef(node) args = [] for arg in node.args: - args.append(frame.symbols.ref(arg)) + args.append(frame.symbols.ref(arg.name)) - undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs')) + undeclared = find_undeclared(node.body, ('caller', 'kwargs', 'varargs')) if 'caller' in undeclared: args.append(frame.symbols.declare_parameter('caller')) macro_ref.accesses_caller = True @@ -513,12 +511,12 @@ class CodeGenerator(NodeVisitor): def macro_def(self, macro_ref, frame): """Dump the macro definition for the def created by macro_body.""" arg_tuple = ', '.join(repr(x.name) for x in macro_ref.node.args) - name = getattr(node, 'name', None) - if len(node.args) == 1: + name = getattr(macro_ref.node, 'name', None) + if len(macro_ref.node.args) == 1: arg_tuple += ',' self.write('Macro(environment, macro, %r, (%s), (' % (name, arg_tuple)) - for arg in node.defaults: + for arg in macro_ref.node.defaults: self.visit(arg, frame) self.write(', ') self.write('), %r, %r, %r)' % ( @@ -765,7 +763,7 @@ class CodeGenerator(NodeVisitor): def visit_Import(self, node, frame): """Visit regular imports.""" - self.writeline('l_%s = ' % node.target, node) + self.writeline('%s = ' % frame.symbols.ref(node.target), node) if frame.toplevel: self.write('context.vars[%r] = ' % node.target) if self.environment.is_async: @@ -807,15 +805,16 @@ class CodeGenerator(NodeVisitor): name, alias = name else: alias = name - self.writeline('l_%s = getattr(included_template, ' - '%r, missing)' % (alias, name)) - self.writeline('if l_%s is missing:' % alias) + self.writeline('%s = getattr(included_template, ' + '%r, missing)' % (frame.symbols.ref(alias), name)) + self.writeline('if %s is missing:' % frame.symbols.ref(alias)) self.indent() - self.writeline('l_%s = environment.undefined(%r %% ' + self.writeline('%s = environment.undefined(%r %% ' 'included_template.__name__, ' 'name=%r)' % - (alias, 'the template %%r (imported on %s) does ' - 'not export the requested name %s' % ( + (frame.symbols.ref(alias), + 'the template %%r (imported on %s) does ' + 'not export the requested name %s' % ( self.position(node), repr(name) ), name)) @@ -829,10 +828,11 @@ class CodeGenerator(NodeVisitor): if var_names: if len(var_names) == 1: name = var_names[0] - self.writeline('context.vars[%r] = l_%s' % (name, name)) + self.writeline('context.vars[%r] = %s' % + (name, frame.symbols.ref(name))) else: self.writeline('context.vars.update({%s})' % ', '.join( - '%r: l_%s' % (name, name) for name in var_names + '%r: %s' % (name, frame.symbols.ref(name)) for name in var_names )) if discarded_names: if len(discarded_names) == 1: @@ -843,6 +843,8 @@ class CodeGenerator(NodeVisitor): 'update((%s))' % ', '.join(imap(repr, discarded_names))) def visit_For(self, node, frame): + # TODO: this shoudl really use two frames: one for the loop body + # and a separate one for the loop else block. loop_frame = frame.inner() # try to figure out if we have an extended loop. An extended loop @@ -880,25 +882,13 @@ class CodeGenerator(NodeVisitor): iteration_indicator = self.temporary_identifier() self.writeline('%s = 1' % iteration_indicator) - # Create a fake parent loop if the else or test section of a - # loop is accessing the special loop variable and no parent loop - # exists. - if frame.symbols.find_ref('loop') is None and 'loop' in find_undeclared( - node.iter_child_nodes(only=('else_', 'test')), ('loop',)): - self.writeline("l_loop = environment.undefined(%r, name='loop')" % - ("'loop' is undefined. the filter section of a loop as well " - "as the else block don't have access to the special 'loop'" - " variable of the current loop. Because there is no parent " - "loop it's undefined. Happened in loop on %s" % - self.position(node))) - self.writeline(self.environment.is_async and 'async for ' or 'for ', node) self.visit(node.target, loop_frame) if extended_loop: if self.environment.is_async: - self.write(', l_loop in await make_async_loop_context(') + self.write(', %s in await make_async_loop_context(' % loop_ref) else: - self.write(', l_loop in LoopContext(') + self.write(', %s in LoopContext(' % loop_ref) else: self.write(' in ') @@ -953,15 +943,16 @@ class CodeGenerator(NodeVisitor): if node.else_: self.writeline('%s = 0' % iteration_indicator) self.outdent() + self.leave_frame(loop_frame) if node.else_: self.writeline('if %s:' % iteration_indicator) self.indent() + self.enter_frame(loop_frame) self.blockvisit(node.else_, loop_frame) + self.leave_frame(loop_frame) self.outdent() - self.leave_frame(loop_frame) - # if the node was recursive we have to return the buffer contents # and start the iteration code if node.recursive: @@ -1000,16 +991,15 @@ class CodeGenerator(NodeVisitor): if not node.name.startswith('_'): self.write('context.exported_vars.add(%r)' % node.name) ref = frame.symbols.ref(node.name) - self.writeline('context.vars[%r] = ' % ref) - self.write('l_%s = ' % node.name) - self.macro_def(macro_ref) + self.writeline('context.vars[%r] = ' % node.name) + self.write('%s = ' % frame.symbols.ref(node.name)) + self.macro_def(macro_ref, macro_frame) frame.assigned_names.add(node.name) def visit_CallBlock(self, node, frame): - children = node.iter_child_nodes(exclude=('call',)) - call_frame, macro_ref = self.macro_body(node, frame, children) + call_frame, macro_ref = self.macro_body(node, frame) self.writeline('caller = ') - self.macro_def(macro_ref) + self.macro_def(macro_ref, call_frame) self.start_write(frame, node) self.visit_Call(node.call, call_frame, forward_caller=True) self.end_write(frame) @@ -1223,17 +1213,15 @@ class CodeGenerator(NodeVisitor): # will disable output checks. This way one can use set blocks # toplevel even in extended templates. block_frame.require_output_check = False - block_frame.inspect(node.body) - aliases = self.push_scope(block_frame) - self.pull_locals(block_frame) + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) self.buffer(block_frame) self.blockvisit(node.body, block_frame) - self.pop_scope(aliases, block_frame) - self.newline(node) self.visit(node.target, frame) self.write(' = concat(%s)' % block_frame.buffer) self.export_assigned_vars(frame) + self.leave_frame(block_frame) # -- Expression Visitors @@ -1491,11 +1479,10 @@ class CodeGenerator(NodeVisitor): def visit_Scope(self, node, frame): scope_frame = frame.inner() - scope_frame.inspect(node.iter_child_nodes()) - aliases = self.push_scope(scope_frame) - self.pull_locals(scope_frame) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) self.blockvisit(node.body, scope_frame) - self.pop_scope(aliases, scope_frame) + self.leave_frame(scope_frame) def visit_EvalContextModifier(self, node, frame): for keyword in node.options: diff --git a/jinja2/debug.py b/jinja2/debug.py index f0eb79e4..9b409c6c 100644 --- a/jinja2/debug.py +++ b/jinja2/debug.py @@ -207,8 +207,11 @@ def get_jinja_locals(real_locals): for name, value in iteritems(real_locals): if not name.startswith('l_') or value is missing: continue - _, depth, name = name.split('_', 2) - depth = int(depth) + try: + _, depth, name = name.split('_', 2) + depth = int(depth) + except ValueError: + continue cur_depth = local_overrides.get(name, (-1,))[0] if cur_depth < depth: local_overrides[name] = (depth, value) diff --git a/jinja2/idtracking.py b/jinja2/idtracking.py index 32fb6e97..be602111 100644 --- a/jinja2/idtracking.py +++ b/jinja2/idtracking.py @@ -132,7 +132,7 @@ class RootVisitor(NodeVisitor): _simple_visit def visit_AssignBlock(self, node): - for child in self.body: + for child in node.body: self.sym_visitor.visit(child) def visit_CallBlock(self, node): @@ -215,6 +215,7 @@ class FrameSymbolVisitor(NodeVisitor): def visit_AssignBlock(self, node, **kwargs): """Stop visiting at block assigns.""" + self.visit(node.target, **kwargs) def visit_Scope(self, node, **kwargs): """Stop visiting at scopes.""" diff --git a/jinja2/meta.py b/jinja2/meta.py index 3dbab7c2..5668e190 100644 --- a/jinja2/meta.py +++ b/jinja2/meta.py @@ -11,7 +11,7 @@ """ from jinja2 import nodes from jinja2.compiler import CodeGenerator -from jinja2._compat import string_types +from jinja2._compat import string_types, iteritems class TrackingCodeGenerator(CodeGenerator): @@ -25,9 +25,12 @@ class TrackingCodeGenerator(CodeGenerator): def write(self, x): """Don't write.""" - def pull_locals(self, frame): + def enter_frame(self, frame): """Remember all undeclared identifiers.""" - self.undeclared_identifiers.update(frame.identifiers.undeclared) + CodeGenerator.enter_frame(self, frame) + for _, (action, param) in iteritems(frame.symbols.loads): + if action == 'resolve': + self.undeclared_identifiers.add(param) def find_undeclared_variables(ast): diff --git a/tests/test_api.py b/tests/test_api.py index f2279ea3..c358d01b 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -329,7 +329,7 @@ class TestLowLevel(): def test_custom_context(self): class CustomContext(Context): - def resolve(self, key): + def resolve_or_missing(self, key): return 'resolve-' + key class CustomEnvironment(Environment):