def compile_withnode(self, srcnode, parent): # WithNodes can be constructed in compilexml with a list of # key-value pairs values = srcnode.get_pairs() values = [(self.unique_id('save'),) + item for item in values] marker = self.unique_id('marker') parent.body.extend(parse_and_strip(u"{} = []".format(marker))) for savevar, varname, value in values: parent.body.append(self.annotate_runtime_errors(parse_and_strip( u"{s} = __piglet_ctx.get('{k}', {m})\n" u"{k} = __piglet_ctx['{k}'] = {v}" .format(s=savevar, k=varname, v=value, m=marker) ), srcnode)) for item in srcnode.children: self._compile(item, parent) for savevar, varname, value in values: parent.body.extend(parse_and_strip( u"if {s} is {m}:\n" u" del __piglet_ctx['{k}']\n" u" {k} = __piglet_rt.Undefined('{k}')\n" u"else:\n" u" {k} = __piglet_ctx['{k}'] = {s}" .format(s=savevar, k=varname, m=marker)))
def compile_call(self, srcnode, parent): fn = srcnode.function if '(' not in fn: fn += '()' call = parse_and_strip(fn)[0].value leftovers = im.ContainerNode() for item in srcnode.children: if isinstance(item, im.CallKeyword): funcname = self.unique_id(item.name) self._compile_function(item, parent, funcname) call.keywords.append(keyword(arg=item.name, value=LoadName(funcname))) elif isinstance(item, im.TextNode) and re.match('^\s*$', item.content, re.S): continue else: leftovers.children.append(item) if leftovers.children: funcname = self.unique_id() self._compile_function(leftovers, parent, funcname) call.args.append(make_arg(funcname)) parent.body.append( self.annotate_runtime_errors( Expr(value=Yield(Call(func=LoadName('str'), args=[call], starargs=None, kwargs=None, keywords=[]))), srcnode))
def compile_fornode(self, srcnode, parent): for_ = parse_and_strip('for {}: pass'.format(srcnode.each))[0] for_.body = [] set_pos(for_, srcnode) parent.body.append(self.annotate_runtime_errors(for_, srcnode)) for item in srcnode.children: self._compile(item, for_)
def compile_choosenode(self, srcnode, parent): if srcnode.test is None or srcnode.test.strip() == '': srcnode.test = 'True' choosevar = self.unique_id('choose') chooseflag = self.unique_id('chosen') parent.body.append(self.annotate_runtime_errors( parse_and_strip(u'{} = False\n' u'{} = {}\n'.format(chooseflag, choosevar, srcnode.test)), srcnode)) for item in srcnode.children: if isinstance(item, im.WhenNode): comparison = get_comparison(u'{} is False and {} == ({})' .format(chooseflag, choosevar, item.test)) if_ = If(test=comparison, body=[], orelse=[]) if_.body.extend( parse_and_strip('{} = True'.format(chooseflag))) set_pos(if_, item) parent.body.append(self.annotate_runtime_errors(if_, srcnode)) for sub in item.children: self._compile(sub, if_) elif isinstance(item, im.OtherwiseNode): comparison = get_comparison('{} is False'.format(chooseflag)) if_ = If(test=comparison, body=[], orelse=[]) set_pos(if_, item) parent.body.append(if_) for sub in item.children: self._compile(sub, if_) if if_.body == []: if_.body = [Pass()] else: self._compile(item, parent)
def _interpolated_str_to_ast_value(source): items = List( [(Str(item) if isinstance(item, (str, ustr)) else parse_and_strip(u'x = ({})'.format(item.value))[0].value) for item in interpolate.parse_interpolations(source)], Load() ) return Call(func=Attribute(Str(''), 'join', Load()), args=[items], starargs=None, kwargs=None, keywords=[])
def compile_defnode(self, srcnode, parent): parent = self.module # Will be either "myfunc(arg1, arg2)" or just plain "myfunc" funcsig = srcnode.function if '(' not in funcsig: funcsig += '()' parsedsig = parse_and_strip('def {}: pass'.format(funcsig))[0] fn = self._compile_function(srcnode, parent, parsedsig.name, descend=False) fn.args = parsedsig.args set_pos(fn, srcnode) # Create an empty block inheritance variables so that subsequent # <py:includes> have them to work with fn.body.extend(parse_and_strip( '__piglet_bases = []\n' '__piglet_extra_blocks = {}\n')) for item in srcnode.children: self._compile(item, fn)
def compile_extendsnode(self, srcnode, parent): if '$' in srcnode.href: value = _interpolated_str_to_ast_value(srcnode.href) parent.body.append(Assign(targets=[StoreName('__piglet_tmp')], value=value)) loadcode = '__piglet_rt.load(__piglet_template, __piglet_tmp)\n' else: loadcode = ('__piglet_rt.load(__piglet_template, "{}")\n' .format(srcnode.href)) parent.body.extend(parse_and_strip( '__piglet_parent = {}' '__piglet_bases = [__piglet_parent] + __piglet_bases\n' .format(loadcode))) for n in srcnode.children: if isinstance(n, im.BlockNode): self.compile_blockreplacenode(n, parent) elif isinstance(n, im.DefNode): self._compile(n, parent) block_ids = [make_block_name(n.name) for n in srcnode.find(im.BlockNode)] parent_template_call = Call( func=LoadAttribute('__piglet_parent', '__piglet_root__'), args=[], starargs=None, kwargs=None, keywords=([keyword(arg='__piglet_bases', value=LoadName('__piglet_bases'))] + [keyword(arg=str(b), value=LoadName(str(b))) for b in block_ids]) ) add_kwarg(parent_template_call, '__piglet_extra_blocks') if YieldFrom is not None: parent.body.append( Expr(value=YieldFrom(value=parent_template_call))) else: loopvar = self.unique_id('loop') parent.body.append( For(target=Name(id=loopvar, ctx=Store()), iter=parent_template_call, body=[Expr(value=Yield(Name(id=loopvar, ctx=Load())))], orelse=[]))
def compile_interpolatenode(self, srcnode, parent, mode='yield'): value = parse_and_strip(u'x = ({})'.format(srcnode.value))[0].value if srcnode.autoescape: escaped = Call(func=Name(id='__piglet_escape', ctx=Load()), args=[value], starargs=None, kwargs=None, keywords=[]) else: escaped = Call(func=Name(id='__piglet_ustr', ctx=Load()), args=[value], starargs=None, kwargs=None, keywords=[]) parent.body.append(self.annotate_runtime_errors( Expr(value=Yield(escaped)), srcnode))
def compile_filternode(self, srcnode, parent): func = parse_and_strip(srcnode.function)[0].value with self.collect_output(parent) as ACC: for node in srcnode.children: self._compile(node, parent) joined = Call(func=Attribute(value=Str(s=''), attr='join', content=Load()), args=[LoadName(ACC)], starargs=None, kwargs=None, keywords=[]) parent.body.append(Expr(value=Yield( Call(func=func, args=[joined], starargs=None, kwargs=None, keywords=[]) )))
def annotate_runtime_errors(self, body, imnode): from piglet.astutil import TryExcept from ast import ExceptHandler if imnode.pos is None: raise ValueError("pos attribute not set on im node") position = self.filename, imnode.pos.line handler = parse_and_strip( 'getattr(__piglet_rtdata, "context", [{{}}])[-1]' '.setdefault("__piglet_exc_locations", []).append(({!r}, {!r}))\n' 'raise\n' .format(*position)) if not isinstance(body, list): body = [body] te = TryExcept(body=body, handlers=[ExceptHandler(type=LoadName('Exception'), name=None, body=handler)]) te.position = self.filename, imnode.pos.line return te
def compile_inlinecodenode(self, srcnode, parent): statements = parse_and_strip(srcnode.pysrc) if statements: set_pos(statements[0], srcnode) parent.body.append( self.annotate_runtime_errors(statements, srcnode))
def compile_blocknode(self, srcnode, parent, mode='define'): """ Compile a BlockNode (<py:block>). :param mode: if 'define' this is a block definition; if 'replace' this is a block being used inside an ExtendsNode to replace the parent template's content. """ assert mode in {'define', 'replace'} block_funcname = block_name = make_block_name(srcnode.name) block_func = self._compile_function(srcnode, self.module, block_funcname, append=False, descend=False) # A template extending a parent template may override # a block without implementing one or more of the nested # subblocks from the parent template. The parent will still pass # references to the block functions when calling the extending # template's block, so we need to be able to accept these. add_arg_default(block_func, make_arg('__piglet_bases'), List(elts=[], ctx=Load())) block_func.args.kwarg = make_kwarg('__piglet_extra_blocks') block_func.body.extend(parse_and_strip(''' super = __piglet_rt.get_super(__piglet_bases, __piglet_template, '{0}') '''.format(block_funcname))) block_func_call = Call( func=LoadName(block_name), args=[], starargs=None, kwargs=None, keywords=[keyword(arg='__piglet_bases', value=LoadName('__piglet_bases'))]) add_kwarg(block_func_call, '__piglet_extra_blocks') # Push the current parent context onto the block chain. If nested # blocks are encountered, we need to be able to insert default # args for inner blocks all the way up the chain. For example:: # # <py:block name="page"> # <py:block name="head"> # </py:block> # </py:block> # # Should generate the function signatures:: # # def __piglet_head0(...): # def __piglet_page0(..., head=__piglet_head0): # def __piglet_root__(..., page=__piglet_page0, head=__piglet_head0): # # When compiling the 'head' block, the compiler has to # go back and insert the 'head=__piglet_head0' arguments into # __piglet_page0 and __piglet_root__. That's what this data structure # is for. self.block_chain.append((self.get_func(parent), block_func_call)) # Insert this before existing functions block functions are # declared before the containing function. This is required because # a reference to the block function is placed in the containing # function's argument list for ix, n in enumerate(self.module.body): if isinstance(n, FunctionDef): self.module.body.insert(ix, block_func) break else: self.module.body.append(block_func) for item in srcnode.children: self._compile(item, block_func) # Recursively add this block's function as a keyword argument # throughout the caller chain for referent, referent_call in self.block_chain: add_arg_default(referent, make_arg(block_name), LoadName(block_funcname)) if referent_call is not block_func_call: referent_call.keywords.append( keyword(arg=block_name, value=LoadName(block_name))) self.block_chain.pop() if mode == 'replace': return if YieldFrom is not None: parent.body.append(Expr(value=YieldFrom(value=block_func_call))) else: loopitervar = self.unique_id('loop') loopvar = self.unique_id('item') # Call the block, equivalent to:: # for i in BLOCK(**__piglet_extra_blocks): # yield i # # We pass **__piglet_extra_blocks into BLOCK as it might have # subblocks defined that the caller doesn't know about parent.body.append( Assign(targets=[StoreName(loopitervar)], value=block_func_call) ) loopiter = Name(id=loopitervar, ctx=Load()) parent.body.append( For(target=Name(id=loopvar, ctx=Store()), iter=loopiter, body=[Expr(value=Yield(Name(id=loopvar, ctx=Load())))], orelse=[]))