def visit_For(self, node, frame): # when calculating the nodes for the inner frame we have to exclude # the iterator contents from it for name in node.find_all(nodes.Name): if name.ctx == 'store' and name.name == 'loop': self.fail('Can\'t assign to special loop variable ' 'in for-loop target', name.lineno) # We rename the special loop variables, to distinguish them # from recursive loop() calls for var in node.find_all(nodes.Getattr): for name in var.find_all(nodes.Name): if name.ctx == 'load': name.name = 'l_loop' if node.else_: iteration_indicator = self.temporary_identifier() special_loop = 'l_loop' in find_undeclared(node.iter_child_nodes(only=('body',)), ('l_loop',)) if node.recursive: self.writeline_js('var loop = function(iter) {', frame, node, whitespace=True, end=True) self.indent_js() if node.else_: self.writeline_js('var %s = 1' % iteration_indicator, frame, node, whitespace=True, end=True) # If we're accessing the special loop variables and there's a filter test on the loop, # we need to do the filtering beforehand if special_loop and node.test is not None: filtered_var = self.temporary_identifier() self.writeline_js('var %s = _.filter(' % filtered_var, frame, node, whitespace=True) if node.recursive: self.write_js('iter', frame) else: self.visit(node.iter, frame) self.write_js(', function(item) { return ', frame) self.visit(node.test, frame) self.write_js_stmt_end(' })', frame, end_quote=True) self.writeline_js('_.each(', frame, node, whitespace=True) if special_loop and node.test is not None: self.write_js(filtered_var, frame) elif node.recursive: self.write_js('iter', frame) else: self.visit(node.iter, frame) self.write_js(', ', frame) self.write_js('function(', frame) self.visit(node.target, frame) self.write_js_stmt_end(', index0, iter) {', frame, end_quote=True) self.indent_js() # If we don't access the special loop variables inside this loop, then any filtering of the # collection becomes a continue if not special_loop and node.test is not None: self.writeline_js('if(!(', frame, node, whitespace=True) self.visit(node.test, frame) self.write_js_stmt_end(')) { continue; }', frame, end_quote=True) if special_loop: self.writeline_js('var l_loop = {index0: index0, index: index0 + 1, first: index0 == 0, length: iter.length}', frame, node, whitespace=True, end=True) self.writeline_js('l_loop.revindex = iter.length - l_loop.index0', frame, node, whitespace=True, end=True) self.writeline_js('l_loop.revindex0 = l_loop.revindex - 1', frame, node, whitespace=True, end=True) self.writeline_js('l_loop.last = l_loop.revindex0 == 0', frame, node, whitespace=True, end=True) self.writeline_js('l_loop.cycle = function() { return arguments.length ? arguments[index0 % arguments.length] : \'\' }}', frame, node, whitespace=True, end=True) for body_node in node.body: self.visit(body_node, frame) if node.else_: self.writeline_js('%s = 0' % iteration_indicator, frame, node, whitespace=True, end=True) self.outdent_js() self.writeline_js('})', frame, node, whitespace=True, end=True) if node.else_: self.writeline_js('if(%s) {' % iteration_indicator, frame, node, whitespace=True, end=True) self.indent_js() for else_node in node.else_: self.visit(else_node, frame) self.outdent_js() self.writeline_js('}', frame, node, whitespace=True, end=True) if node.recursive: self.outdent_js() self.writeline_js('}', frame, end=True, whitespace=True) self.writeline_js('loop(', frame, node, whitespace=True) self.visit(node.iter, frame) self.write_js_stmt_end(')', frame, end_quote=True)
def visit_Template(self, node, frame=None): eval_ctx = EvalContext(self.environment, self.name) # find all blocks for block in node.find_all(nodes.Block): if block.name in self.blocks: self.fail('block %r defined twice' % block.name, block.lineno) self.blocks[block.name] = block for import_ in node.find_all(nodes.ImportedName): assert False, "imports not supported" self.safename = self.name.replace(".","_") self.writeline('var tpl_%s = new function()' % self.safename) self.indent() frame = Frame(eval_ctx) frame.inspect(node.body) frame.toplevel = frame.rootlevel = True frame.require_output_check = not self.has_known_extends self.buffer(frame) # at this point we now have the blocks collected and can visit them too. for name, block in self.blocks.iteritems(): block_frame = Frame(eval_ctx) block_frame.inspect(block.body) block_frame.block = name block_frame.buffer = frame.buffer self.writeline('var block_%s = function(context, %s)' % (name, frame.buffer), block, 1) self.indent() undeclared = find_undeclared(block.body, ('self', 'super')) if 'self' in undeclared: block_frame.identifiers.add_special('self') self.writeline('l_self = TemplateReference(context)') if 'super' in undeclared: block_frame.identifiers.add_special('super') self.writeline('l_super = context.super_block(block_%s)' % name) self.pull_locals(block_frame) self.pull_dependencies(block.body) self.blockvisit(block.body, block_frame) self.outdent() self.writeline('var blocks = {%s};' % ', '.join('%r: block_%s' % (x, x) for x in self.blocks), extra=1) self.writeline("var name = %r"% self.name) self.writeline('return') self.indent() self.writeline('name: name,') self.writeline('root: function(context)', extra=1) self.indent() self.writeline('if(context.blocks==undefined)') self.indent() self.writeline("context.blocks=blocks") self.outdent() self.clear_buffer(frame) self.writeline('var parent_template = null;') if 'self' in find_undeclared(node.body, ('self',)): frame.identifiers.add_special('self') self.writeline('l_self = context.call_blocks()') self.pull_locals(frame) self.pull_dependencies(node.body) self.blockvisit(node.body, frame) self.writeline("if(parent_template)") self.indent() self.writeline('return parent_template.root(context)') self.outdent() self.return_buffer_contents(frame) self.outdent() """ if not self.has_known_extends: self.indent() self.writeline('if parent_template is not None:') self.indent() """ self.write(',') self.writeline('blocks: blocks') self.outdent() self.outdent() self.writeline('if(typeof(environment)!="undefined")') self.indent() self.writeline('environment.tpl[%r] = tpl_%s' % (self.name, self.safename)) self.outdent()
def visit_For(self, node, frame): # when calculating the nodes for the inner frame we have to exclude # the iterator contents from it children = node.iter_child_nodes(exclude=('iter',)) if node.recursive: loop_frame = self.function_scoping(node, frame, children, find_special=False) else: loop_frame = frame.inner() loop_frame.inspect(children) # try to figure out if we have an extended loop. An extended loop # is necessary if the loop is in recursive mode if the special loop # variable is accessed in the body. extended_loop = node.recursive or 'loop' in \ find_undeclared(node.iter_child_nodes( only=('body',)), ('loop',)) aliases = self.push_scope(loop_frame, ('loop',)) # make sure the loop variable is a special one and raise a template # assertion error if a loop tries to write to loop if extended_loop: loop_frame.identifiers.add_special('loop') for name in node.find_all(nodes.Name): if name.ctx == 'store' and name.name == 'loop': self.fail('Can\'t assign to special loop variable ' 'in for-loop target', name.lineno) self.pull_locals(loop_frame) if node.else_: 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 'loop' not in aliases 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("var __iter_map=function(")#__current,__i)") if not isinstance(node.target, nodes.Tuple): self.visit(node.target, loop_frame) else: self.write("__current") self.write(",__i)") self.indent() #XXX: this sucks if extended_loop: self.writeline("var l_loop= new environment.Loop(__i,"); self.visit(node.iter, loop_frame) self.write(".length, __iter_map);") def setvar(node_target, node_iter, idx=None): self.writeline("var ", node_target); self.visit(node_target, loop_frame); self.write(" = ") self.write("__current[%d];"%idx) if isinstance(node.target, nodes.Tuple): for idx,item in enumerate(node.target.items): setvar(item, node.iter, idx) if node.test is not None: self.writeline('if(! ') self.visit(node.test, loop_frame) self.write(') return') self.blockvisit(node.body, loop_frame) if node.else_: self.writeline('%s = 0' % iteration_indicator) self.outdent() self.writeline("if((") self.visit(node.iter, loop_frame) self.write("!==undefined)&&") self.visit(node.iter, loop_frame) self.write(".map)") self.visit(node.iter, loop_frame) self.write(".map(__iter_map)") self.writeline("else if((") self.visit(node.iter, loop_frame) self.write("!==undefined)&&") self.visit(node.iter, loop_frame) self.write(".length)") self.indent() self.writeline("for(var __i=0;__i<") self.visit(node.iter, loop_frame) self.write(".length;__i++)") self.indent() self.writeline("__iter_map(") self.visit(node.iter, loop_frame) self.write("[__i], __i)") self.outdent() self.outdent() if node.else_: self.writeline('if(%s)' % iteration_indicator) self.indent() self.blockvisit(node.else_, loop_frame) self.outdent() self.pop_scope(aliases, loop_frame)
def template_ast(self, node, frame=None): # pragma: no cover """ Shim for Jinja2's default ``Jinja``-sytnax-to-Python AST converter. Wraps template code in a module-level ``run`` function that binds it to an instance of :py:class:`jinja2.Environment`. :param node: Current AST node. :param frame: Current code frame. :return: ``None``. """ assert frame is None, "no root frame allowed" eval_ctx = EvalContext(self.environment, self.name) from jinja2.runtime import __all__ as exported self.writeline("# -*- coding: utf-8 -*-") self.writeline("") self.writeline("from __future__ import division") self.writeline("from jinja2.runtime import " + ", ".join(exported)) if not unoptimize_before_dead_code: self.writeline("dummy = lambda *x: None") # if we want a deferred initialization we cannot move the # environment into a local name envenv = not self.defer_init and ", environment=environment" or "" # do we have an extends tag at all? If not, we can save some # overhead by just not processing any inheritance code. have_extends = node.find(nodes.Extends) is not None # find all blocks for block in node.find_all(nodes.Block): if block.name in self.blocks: self.fail("block %r defined twice" % block.name, block.lineno) self.blocks[block.name] = block # find all imports and import them for import_ in node.find_all(nodes.ImportedName): if import_.importname not in self.import_aliases: imp = import_.importname self.import_aliases[imp] = alias = self.temporary_identifier() if "." in imp: module, obj = imp.rsplit(".", 1) self.writeline("from %s import %s as %s" % (module, obj, alias)) else: self.writeline("import %s as %s" % (imp, alias)) # add the load name self.writeline("name = %r" % self.name) # generate the deferred init wrapper self.writeline("def run(environment):", extra=1) self.indent() # generate the root render function. self.writeline("def root(context%s):" % envenv, extra=1) # process the root frame = Frame(eval_ctx) frame.inspect(node.body) frame.toplevel = frame.rootlevel = True frame.require_output_check = have_extends and not self.has_known_extends self.indent() if have_extends: self.writeline("parent_template = None") if "self" in find_undeclared(node.body, ("self",)): frame.identifiers.add_special("self") self.writeline("l_self = TemplateReference(context)") self.pull_locals(frame) self.pull_dependencies(node.body) self.blockvisit(node.body, frame) self.outdent() # make sure that the parent root is called. if have_extends: if not self.has_known_extends: self.indent() self.writeline("if parent_template is not None:") self.indent() self.writeline("for event in parent_template." "root_render_func(context):") self.indent() self.writeline("yield event") self.outdent(2 + (not self.has_known_extends)) # at this point we now have the blocks collected and can visit them too. for name, block in iteritems(self.blocks): block_frame = Frame(eval_ctx) block_frame.inspect(block.body) block_frame.block = name self.writeline("def block_%s(context%s):" % (name, envenv), block, 1) self.indent() undeclared = find_undeclared(block.body, ("self", "super")) if "self" in undeclared: block_frame.identifiers.add_special("self") self.writeline("l_self = TemplateReference(context)") if "super" in undeclared: block_frame.identifiers.add_special("super") self.writeline("l_super = context.super(%r, " "block_%s)" % (name, name)) self.pull_locals(block_frame) self.pull_dependencies(block.body) self.blockvisit(block.body, block_frame) self.outdent() self.writeline("blocks = {%s}" % ", ".join("%r: block_%s" % (x, x) for x in self.blocks), extra=1) # add a function that returns the debug info self.writeline("debug_info = %r" % "&".join("%s=%s" % x for x in self.debug_info)) self.writeline("return (root, blocks, debug_info)") self.outdent() self.writeline("")