def _ensure_all_functions_yield(module): """ All generated functions should contain at least one yield statement. This walks the ast to insert a "yield ''" in functions that don't otherwise produce output (eg in the case of '<py:def function="a"></py:def>') """ functions = {} if YieldFrom is not None: yield_classes = (Yield, YieldFrom) else: yield_classes = (Yield,) for node, ancestors in astwalk(module): if isinstance(node, FunctionDef): functions.setdefault(node, False) elif isinstance(node, yield_classes): f = next(a for a in reversed(ancestors) if isinstance(a, FunctionDef)) functions[f] = True for f in functions: if not functions[f]: f.body.append(Expr(Yield(Str(s='')))) return module
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_translationnode(self, srcnode, parent): translated = Call(func=LoadName('_'), args=[Str(srcnode.get_msgstr())], starargs=None, kwargs=None, keywords=[]) named_children = [(name, node) for name, node in srcnode.named_children() if name is not None] if not named_children: # Simple case - no dynamic children for placeholder replacement parent.body.append(Expr(value=Yield(translated))) return parent.body.append( Assign(targets=[StoreName('__piglet_places')], value=Dict([], [])) ) for name, node in named_children: with self.collect_output(parent) as ACC: self._compile(node, parent) parent.body.append( Assign(targets=[Subscript(value=LoadName('__piglet_places'), slice=Index(value=Str(name)), ctx=Store())], value=Call(func=Attribute(value=Str(s=''), attr='join', ctx=Load()), args=[LoadName(ACC)], starargs=None, kwargs=None, keywords=[])) ) for name, node in named_children: translated = Call( func=Attribute(value=translated, attr='replace', ctx=Load()), args=[Str('${{{}}}'.format(name)), Subscript(value=LoadName('__piglet_places'), slice=Index(value=Str(name)), ctx=Load())], starargs=None, kwargs=None, keywords=[]) set_pos(translated, srcnode) parent.body.append(Expr(value=Yield(translated)))
def visit_Yield(self, node): existing_node = self.generic_visit(node) value = existing_node.value if value is None: value = Name(id='None', ctx=Load()) return Yield(value=self._create_bare_context_call( 'yield_value', [value, Num(n=existing_node.lineno)]))
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 parseStatement(parser): if parser.matchLexeme(keywords['LET']): return parseLetStatement(parser) elif parser.matchLexeme(keywords['IF']): return parseIfStatement(parser) elif parser.matchLexeme(keywords['EACH']): return parseEachStatement(parser) elif parser.matchLexeme(keywords['ECHO']): parser.next() if parser.matchTokensort(PRESTRING): exp = parseEmbedding(parser) else: exp = parseExpression(parser) parser.check(lexeme=";") parser.next() # skip ; return Echo(expression=exp, lineo=parser.currentToken[2]) elif parser.matchLexeme(keywords['CDATA']): parser.next() expression = parseExpression(parser) return Cdata(expression) elif parser.matchLexeme('{'): #? starts a new staments block return parseStatementBlock(parser) elif parser.matchLexeme(keywords['COMMENT']): parser.next(tokensort=STRING) comment = Comment(parser.currentToken[1], lineo=parser.currentToken[2]) parser.next() parser.next() return comment elif parser.matchLexeme(keywords['YIELD']): parser.next(lexeme=";") parser.next() return Yield() elif parser.matchTokensort(NAME): markup = parseMarkupStatement(parser) return markup elif parser.matchTokensort(ENDMARKER): #needed? return raise SyntaxError( parser.currentToken, expected="""statement, "if", "each", "let", "{", "comment", "echo", "cdata", "yield" or Markup""")
def visit_Return(self, node: Return) -> Any: value = node.value if mask is not None: value = BinOp(left=value, op=BitAnd(), right=constant(mask)) return Expr(value=Yield(value=value))
def yield_value(self, value, srcnode=None): expr = Expr(value=Yield(value)) if srcnode: set_pos(expr, srcnode) return expr
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=[]))
def compile_textnode(self, srcnode, parent): expr = Expr(value=Yield(Str(srcnode.content))) set_pos(expr, srcnode) parent.body.append(expr)