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=[]))