def parse(self, parser):
        lineno = next(parser.stream).lineno

        files = []
        output = nodes.Const(None)
        filters = nodes.Const(None)
        dbg = nodes.Const(None)
        depends = nodes.Const(None)

        # Parse the arguments
        first = True
        while parser.stream.current.type != 'block_end':
            if not first:
                parser.stream.expect('comma')
            first = False

            # Lookahead to see if this is an assignment (an option)
            if parser.stream.current.test('name') and parser.stream.look().test('assign'):
                name = next(parser.stream).value
                parser.stream.skip()
                value = parser.parse_expression()
                if name == 'filters':
                    filters = value
                elif name == 'filter':
                    filters = value
                    warnings.warn('The "filter" option of the {%% assets %%} '
                                  'template tag has been renamed to '
                                  '"filters" for consistency reasons '
                                  '(line %s).' % lineno,
                                    ImminentDeprecationWarning)
                elif name == 'output':
                    output = value
                elif name == 'debug':
                    dbg = value
                elif name == 'depends':
                    depends = value
                else:
                    parser.fail('Invalid keyword argument: %s' % name)
            # Otherwise assume a source file is given, which may be any
            # expression, except note that strings are handled separately above.
            else:
                expression = parser.parse_expression()
                if isinstance(expression, (nodes.List, nodes.Tuple)):
                    files.extend(expression.iter_child_nodes())
                else:
                    files.append(expression)

        # Parse the contents of this tag
        body = parser.parse_statements(['name:endassets'], drop_needle=True)

        # We want to make some values available to the body of our tag.
        # Specifically, the file url(s) (ASSET_URL), and any extra dict set in
        # the bundle (EXTRA).
        #
        # A short interlope: I would have preferred to make the values of the
        # extra dict available directly. Unfortunately, the way Jinja2 does
        # things makes this problematic. I'll explain.
        #
        # Jinja2 generates Python code from it's AST which it then executes.
        # So the way extensions implement making custom variables available to
        # a block of code is by generating a ``CallBlock``, which essentially
        # wraps our child nodes in a Python function. The arguments of this
        # function are the values that are available to our tag contents.
        #
        # But we need to generate this ``CallBlock`` now, during parsing, and
        # right now we don't know the actual ``Bundle.extra`` values yet. We
        # only resolve the bundle during rendering!
        #
        # This would easily be solved if Jinja2 where to allow extensions to
        # scope it's context, which is a dict of values that templates can
        # access, just like in Django (you might see on occasion
        # ``context.resolve('foo')`` calls in Jinja2's generated code).
        # However, it seems the context is essentially only for the initial
        # set of data passed to render(). There are some statements by Armin
        # that this might change at some point, but I've run into this problem
        # before, and I'm not holding my breath.
        #
        # I **really** did try to get around this, including crazy things like
        # inserting custom Python code by patching the tag child nodes::
        #
        #        rv = object.__new__(nodes.InternalName)
        #        # l_EXTRA is the argument we defined for the CallBlock/Macro
        #        # Letting Jinja define l_kwargs is also possible
        #        nodes.Node.__init__(rv, '; context.vars.update(l_EXTRA)',
        #                            lineno=lineno)
        #        # Scope required to ensure our code on top
        #        body = [rv, nodes.Scope(body)]
        #
        # This custom code would run at the top of the function in which the
        # CallBlock node would wrap the code generated from our tag's child
        # nodes. Note that it actually does works, but doesn't clear the values
        # at the end of the scope).
        #
        # If it is possible to do this, it certainly isn't reasonable/
        #
        # There is of course another option altogether: Simple resolve the tag
        # definition to a bundle right here and now, thus get access to the
        # extra dict, make all values arguments to the CallBlock (Limited to
        # 255 arguments to a Python function!). And while that would work fine
        # in 99% of cases, it wouldn't be correct. The compiled template could
        # be cached and used with different bundles/environments, and this
        # would require the bundle to resolve at parse time, and hardcode it's
        # extra values.
        #
        # Interlope end.
        #
        # Summary: We have to be satisfied with a single EXTRA variable.
        args = [nodes.Name('ASSET_URL', 'param'),
                nodes.Name('ASSET_SRI', 'param'),
                nodes.Name('EXTRA', 'param')]

        # Return a ``CallBlock``, which means Jinja2 will call a Python method
        # of ours when the tag needs to be rendered. That method can then
        # render the template body.
        call = self.call_method(
            # Note: Changing the args here requires updating ``Jinja2Loader``
            '_render_assets', args=[filters, output, dbg, depends, nodes.List(files)])
        call_block = nodes.CallBlock(call, args, [], body)
        call_block.set_lineno(lineno)
        return call_block
Example #2
0
    def parse(self, parser):
        """
        Parse the referred template and the namespace.
        """
        token = parser.stream.next()
        lineno = token.lineno
        parser.stream.expect('name:to')
        template = parser.parse_expression()
        parser.stream.expect('name:as')
        namespace = parser.stream.next().value
        includeNode = nodes.Include(lineno=lineno)
        includeNode.with_context = True
        includeNode.ignore_missing = False
        includeNode.template = template

        temp = parser.free_identifier(lineno)

        return [
            nodes.Assign(nodes.Name(temp.name, 'store'),
                         nodes.Name(MARKINGS, 'load')).set_lineno(lineno),
            nodes.Assign(nodes.Name(MARKINGS, 'store'),
                         nodes.Const({})).set_lineno(lineno),
            nodes.Assign(nodes.Name(namespace, 'store'),
                         nodes.Const({})).set_lineno(lineno),
            nodes.CallBlock(
                self.call_method('_push_resource',
                                 args=[
                                     nodes.Name(namespace, 'load'),
                                     nodes.Name('site', 'load'),
                                     nodes.Name('resource', 'load'), template
                                 ]), [], [], []).set_lineno(lineno),
            nodes.Assign(
                nodes.Name('resource', 'store'),
                nodes.Getitem(nodes.Name(namespace, 'load'),
                              nodes.Const('resource'),
                              'load')).set_lineno(lineno),
            nodes.CallBlock(
                self.call_method('_assign_reference',
                                 args=[
                                     nodes.Name(MARKINGS, 'load'),
                                     nodes.Name(namespace, 'load')
                                 ]), [], [], [includeNode]).set_lineno(lineno),
            nodes.Assign(
                nodes.Name('resource', 'store'),
                nodes.Getitem(nodes.Name(namespace, 'load'),
                              nodes.Const('parent_resource'),
                              'load')).set_lineno(lineno),
            nodes.Assign(nodes.Name(MARKINGS, 'store'),
                         nodes.Name(temp.name, 'load')).set_lineno(lineno),
        ]
Example #3
0
    def parse(self, parser):
        """Parse a translatable tag."""
        lineno = next(parser.stream).lineno
        num_called_num = False

        # find all the variables referenced.  Additionally a variable can be
        # defined in the body of the trans block too, but this is checked at
        # a later state.
        plural_expr = None
        plural_expr_assignment = None
        variables = {}
        trimmed = None
        while parser.stream.current.type != 'block_end':
            if variables:
                parser.stream.expect('comma')

            # skip colon for python compatibility
            if parser.stream.skip_if('colon'):
                break

            name = parser.stream.expect('name')
            if name.value in variables:
                parser.fail('translatable variable %r defined twice.' %
                            name.value,
                            name.lineno,
                            exc=TemplateAssertionError)

            # expressions
            if parser.stream.current.type == 'assign':
                next(parser.stream)
                variables[name.value] = var = parser.parse_expression()
            elif trimmed is None and name.value in ('trimmed', 'notrimmed'):
                trimmed = name.value == 'trimmed'
                continue
            else:
                variables[name.value] = var = nodes.Name(name.value, 'load')

            if plural_expr is None:
                if isinstance(var, nodes.Call):
                    plural_expr = nodes.Name('_trans', 'load')
                    variables[name.value] = plural_expr
                    plural_expr_assignment = nodes.Assign(
                        nodes.Name('_trans', 'store'), var)
                else:
                    plural_expr = var
                num_called_num = name.value == 'num'

        parser.stream.expect('block_end')

        plural = None
        have_plural = False
        referenced = set()

        # now parse until endtrans or pluralize
        singular_names, singular = self._parse_block(parser, True)
        if singular_names:
            referenced.update(singular_names)
            if plural_expr is None:
                plural_expr = nodes.Name(singular_names[0], 'load')
                num_called_num = singular_names[0] == 'num'

        # if we have a pluralize block, we parse that too
        if parser.stream.current.test('name:pluralize'):
            have_plural = True
            next(parser.stream)
            if parser.stream.current.type != 'block_end':
                name = parser.stream.expect('name')
                if name.value not in variables:
                    parser.fail('unknown variable %r for pluralization' %
                                name.value,
                                name.lineno,
                                exc=TemplateAssertionError)
                plural_expr = variables[name.value]
                num_called_num = name.value == 'num'
            parser.stream.expect('block_end')
            plural_names, plural = self._parse_block(parser, False)
            next(parser.stream)
            referenced.update(plural_names)
        else:
            next(parser.stream)

        # register free names as simple name expressions
        for var in referenced:
            if var not in variables:
                variables[var] = nodes.Name(var, 'load')

        if not have_plural:
            plural_expr = None
        elif plural_expr is None:
            parser.fail('pluralize without variables', lineno)

        if trimmed is None:
            trimmed = self.environment.policies['ext.i18n.trimmed']
        if trimmed:
            singular = self._trim_whitespace(singular)
            if plural:
                plural = self._trim_whitespace(plural)

        node = self._make_node(singular, plural, variables, plural_expr,
                               bool(referenced), num_called_num
                               and have_plural)
        node.set_lineno(lineno)
        if plural_expr_assignment is not None:
            return [plural_expr_assignment, node]
        else:
            return node
Example #4
0
    def parse(self, parser):
        """Parse a translatable tag."""
        lineno = next(parser.stream).lineno
        num_called_num = False
        plural_expr = None
        variables = {}
        while parser.stream.current.type != 'block_end':
            if variables:
                parser.stream.expect('comma')
            if parser.stream.skip_if('colon'):
                break
            name = parser.stream.expect('name')
            if name.value in variables:
                parser.fail('translatable variable %r defined twice.' %
                            name.value,
                            name.lineno,
                            exc=TemplateAssertionError)
            if parser.stream.current.type == 'assign':
                next(parser.stream)
                variables[name.value] = var = parser.parse_expression()
            else:
                variables[name.value] = var = nodes.Name(name.value, 'load')
            if plural_expr is None:
                plural_expr = var
                num_called_num = name.value == 'num'

        parser.stream.expect('block_end')
        plural = plural_names = None
        have_plural = False
        referenced = set()
        singular_names, singular = self._parse_block(parser, True)
        if singular_names:
            referenced.update(singular_names)
            if plural_expr is None:
                plural_expr = nodes.Name(singular_names[0], 'load')
                num_called_num = singular_names[0] == 'num'
        if parser.stream.current.test('name:pluralize'):
            have_plural = True
            next(parser.stream)
            if parser.stream.current.type != 'block_end':
                name = parser.stream.expect('name')
                if name.value not in variables:
                    parser.fail('unknown variable %r for pluralization' %
                                name.value,
                                name.lineno,
                                exc=TemplateAssertionError)
                plural_expr = variables[name.value]
                num_called_num = name.value == 'num'
            parser.stream.expect('block_end')
            plural_names, plural = self._parse_block(parser, False)
            next(parser.stream)
            referenced.update(plural_names)
        else:
            next(parser.stream)
        for var in referenced:
            if var not in variables:
                variables[var] = nodes.Name(var, 'load')

        if not have_plural:
            plural_expr = None
        elif plural_expr is None:
            parser.fail('pluralize without variables', lineno)
        node = self._make_node(singular, plural, variables, plural_expr,
                               bool(referenced), num_called_num
                               and have_plural)
        node.set_lineno(lineno)
        return node
Example #5
0
 def _set_var(var_name, var_value, lineno):
     target_var = nodes.Name(var_name, 'store', lineno=lineno)
     return nodes.Assign(target_var, var_value, lineno=lineno)
Example #6
0
 def parse(self, parser):
     lineno = parser.stream.expect('name:csrf_token').lineno
     call = self.call_method(
         '_render', [nodes.Name('csrf_token', 'load', lineno=lineno)],
         lineno=lineno)
     return nodes.Output([nodes.MarkSafe(call)])
Example #7
0
 def parse(self, parser):
     lineno = six.next(parser.stream).lineno
     return nodes.Output([
         self.call_method('_render', [nodes.Name('csrf_token', 'load')]),
     ]).set_lineno(lineno)
Example #8
0
    def parse(self, parser):
        stream = parser.stream

        tag = six.next(stream)

        # get view name
        if stream.current.test('string'):
            # Need to work around Jinja2 syntax here. Jinja by default acts
            # like Python and concats subsequent strings. In this case
            # though, we want {% url "app.views.post" "1" %} to be treated
            # as view + argument, while still supporting
            # {% url "app.views.post"|filter %}. Essentially, what we do is
            # rather than let ``parser.parse_primary()`` deal with a "string"
            # token, we do so ourselves, and let parse_expression() handle all
            # other cases.
            if stream.look().test('string'):
                token = six.next(stream)
                viewname = nodes.Const(token.value, lineno=token.lineno)
            else:
                viewname = parser.parse_expression()
        else:
            # parse valid tokens and manually build a string from them
            bits = []
            name_allowed = True
            while True:
                if stream.current.test_any('dot', 'sub', 'colon'):
                    bits.append(six.next(stream))
                    name_allowed = True
                elif stream.current.test('name') and name_allowed:
                    bits.append(six.next(stream))
                    name_allowed = False
                else:
                    break
            viewname = nodes.Const("".join([b.value for b in bits]))
            if not bits:
                raise TemplateSyntaxError(
                    "'%s' requires path to view" % tag.value, tag.lineno)

        # get arguments
        args = []
        kwargs = []
        while not stream.current.test_any('block_end', 'name:as'):
            if args or kwargs:
                stream.expect('comma')
            if stream.current.test('name') and stream.look().test('assign'):
                key = nodes.Const(six.next(stream).value)
                stream.skip()
                value = parser.parse_expression()
                kwargs.append(nodes.Pair(key, value, lineno=key.lineno))
            else:
                args.append(parser.parse_expression())

        def make_call_node(*kw):
            return self.call_method('_reverse',
                                    args=[
                                        viewname,
                                        nodes.List(args),
                                        nodes.Dict(kwargs),
                                        nodes.Name('_current_app', 'load'),
                                    ],
                                    kwargs=kw)

        # if an as-clause is specified, write the result to context...
        if stream.next_if('name:as'):
            var = nodes.Name(stream.expect('name').value, 'store')
            call_node = make_call_node(
                nodes.Keyword('fail', nodes.Const(False)))
            return nodes.Assign(var, call_node)
        # ...otherwise print it out.
        else:
            return nodes.Output([make_call_node()]).set_lineno(tag.lineno)
Example #9
0
    def parse(self, parser):
        """Parse a translatable tag."""
        lineno = next(parser.stream).lineno

        # find all the variables referenced.  Additionally a variable can be
        # defined in the body of the trans block too, but this is checked at
        # a later state.
        plural_expr = None
        variables = {}
        while parser.stream.current.type != 'block_end':
            if variables:
                parser.stream.expect('comma')

            # skip colon for python compatibility
            if parser.stream.skip_if('colon'):
                break

            name = parser.stream.expect('name')
            if name.value in variables:
                parser.fail('translatable variable %r defined twice.' %
                            name.value, name.lineno,
                            exc=TemplateAssertionError)

            # expressions
            if parser.stream.current.type == 'assign':
                next(parser.stream)
                variables[name.value] = var = parser.parse_expression()
            else:
                variables[name.value] = var = nodes.Name(name.value, 'load')
            if plural_expr is None:
                plural_expr = var

        parser.stream.expect('block_end')

        plural = plural_names = None
        have_plural = False
        referenced = set()

        # now parse until endtrans or pluralize
        singular_names, singular = self._parse_block(parser, True)
        if singular_names:
            referenced.update(singular_names)
            if plural_expr is None:
                plural_expr = nodes.Name(singular_names[0], 'load')

        # if we have a pluralize block, we parse that too
        if parser.stream.current.test('name:pluralize'):
            have_plural = True
            next(parser.stream)
            if parser.stream.current.type != 'block_end':
                name = parser.stream.expect('name')
                if name.value not in variables:
                    parser.fail('unknown variable %r for pluralization' %
                                name.value, name.lineno,
                                exc=TemplateAssertionError)
                plural_expr = variables[name.value]
            parser.stream.expect('block_end')
            plural_names, plural = self._parse_block(parser, False)
            next(parser.stream)
            referenced.update(plural_names)
        else:
            next(parser.stream)

        # register free names as simple name expressions
        for var in referenced:
            if var not in variables:
                variables[var] = nodes.Name(var, 'load')

        # no variables referenced?  no need to escape
        if not referenced:
            singular = singular.replace('%%', '%')
            if plural:
                plural = plural.replace('%%', '%')

        if not have_plural:
            plural_expr = None
        elif plural_expr is None:
            parser.fail('pluralize without variables', lineno)

        if variables:
            variables = nodes.Dict([nodes.Pair(nodes.Const(x, lineno=lineno), y)
                                    for x, y in variables.items()])
        else:
            variables = None

        node = self._make_node(singular, plural, variables, plural_expr)
        node.set_lineno(lineno)
        return node
def test_complex():
    title_block = nodes.Block(
        'title', [nodes.Output([nodes.TemplateData(u'Page Title')])], False)

    render_title_macro = nodes.Macro(
        'render_title', [nodes.Name('title', 'param')], [], [
            nodes.Output([
                nodes.TemplateData(u'\n  <div class="title">\n    <h1>'),
                nodes.Name('title', 'load'),
                nodes.TemplateData(u'</h1>\n    <p>'),
                nodes.Name('subtitle', 'load'),
                nodes.TemplateData(u'</p>\n    ')
            ]),
            nodes.Assign(nodes.Name('subtitle', 'store'),
                         nodes.Const('something else')),
            nodes.Output([
                nodes.TemplateData(u'\n    <p>'),
                nodes.Name('subtitle', 'load'),
                nodes.TemplateData(u'</p>\n  </div>\n'),
                nodes.If(nodes.Name('something', 'load'), [
                    nodes.Assign(
                        nodes.Name('title_upper', 'store'),
                        nodes.Filter(nodes.Name('title', 'load'), 'upper', [],
                                     [], None, None)),
                    nodes.Output([
                        nodes.Name('title_upper', 'load'),
                        nodes.Call(nodes.Name('render_title', 'load'),
                                   [nodes.Const('Aha')], [], None, None)
                    ])
                ], [], [])
            ])
        ])

    for_loop = nodes.For(nodes.Name('item', 'store'), nodes.Name(
        'seq', 'load'), [
            nodes.Output([
                nodes.TemplateData(u'\n    <li>'),
                nodes.Name('item', 'load'),
                nodes.TemplateData(u'</li>\n    <span>')
            ]),
            nodes.Include(nodes.Const('helper.html'), True, False),
            nodes.Output([nodes.TemplateData(u'</span>\n  ')])
        ], [], None, False)

    body_block = nodes.Block('body', [
        nodes.Output([
            nodes.TemplateData(u'\n  '),
            nodes.Call(nodes.Name('render_title', 'load'),
                       [nodes.Name('item', 'load')], [], None, None),
            nodes.TemplateData(u'\n  <ul>\n  ')
        ]), for_loop,
        nodes.Output([nodes.TemplateData(u'\n  </ul>\n')])
    ], False)

    tmpl = nodes.Template([
        nodes.Extends(nodes.Const('layout.html')),
        title_block,
        render_title_macro,
        body_block,
    ])

    tmpl_sym = symbols_for_node(tmpl)
    assert tmpl_sym.refs == {
        'render_title': 'l_0_render_title',
    }
    assert tmpl_sym.loads == {
        'l_0_render_title': ('undefined', None),
    }
    assert tmpl_sym.stores == set(['render_title'])
    assert tmpl_sym.dump_stores() == {
        'render_title': 'l_0_render_title',
    }

    macro_sym = symbols_for_node(render_title_macro, tmpl_sym)
    assert macro_sym.refs == {
        'subtitle': 'l_1_subtitle',
        'something': 'l_1_something',
        'title': 'l_1_title',
        'title_upper': 'l_1_title_upper',
    }
    assert macro_sym.loads == {
        'l_1_subtitle': ('resolve', 'subtitle'),
        'l_1_something': ('resolve', 'something'),
        'l_1_title': ('param', None),
        'l_1_title_upper': ('resolve', 'title_upper'),
    }
    assert macro_sym.stores == set(['title', 'title_upper', 'subtitle'])
    assert macro_sym.find_ref('render_title') == 'l_0_render_title'
    assert macro_sym.dump_stores() == {
        'title': 'l_1_title',
        'title_upper': 'l_1_title_upper',
        'subtitle': 'l_1_subtitle',
        'render_title': 'l_0_render_title',
    }

    body_sym = symbols_for_node(body_block)
    assert body_sym.refs == {
        'item': 'l_0_item',
        'seq': 'l_0_seq',
        'render_title': 'l_0_render_title',
    }
    assert body_sym.loads == {
        'l_0_item': ('resolve', 'item'),
        'l_0_seq': ('resolve', 'seq'),
        'l_0_render_title': ('resolve', 'render_title'),
    }
    assert body_sym.stores == set([])

    for_sym = symbols_for_node(for_loop, body_sym)
    assert for_sym.refs == {
        'item': 'l_1_item',
    }
    assert for_sym.loads == {
        'l_1_item': ('param', None),
    }
    assert for_sym.stores == set(['item'])
    assert for_sym.dump_stores() == {
        'item': 'l_1_item',
    }
Example #11
0
 def _parse_query(self, parser, lineno):
     name = parser.parse_assign_target(with_tuple=False).name
     body = parser.parse_statements(['name:endquery'], drop_needle=True)
     # name, params, defaults, body
     return nodes.Macro(name, [nodes.Name('__blocks__', 'param')], [],
                        body).set_lineno(lineno)
Example #12
0
        >>> env.compile_expression('var', undefined_to_none=False)()
        Undefined

        **new in Jinja 2.1**
        """
        parser = Parser(self, source, state='variable')
        try:
            expr = parser.parse_expression()
            if not parser.stream.eos:
                raise TemplateSyntaxError('chunk after expression',
                                          parser.stream.current.lineno, None,
                                          None)
        except TemplateSyntaxError, e:
            e.source = source
            raise e
        body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
        template = self.from_string(nodes.Template(body, lineno=1))
        return TemplateExpression(template, undefined_to_none)

    def join_path(self, template, parent):
        """Join a template with the parent.  By default all the lookups are
        relative to the loader root so this method returns the `template`
        parameter unchanged, but if the paths should be relative to the
        parent template, this function can be used to calculate the real
        template name.

        Subclasses may override this method and implement template path
        joining here.
        """
        return template
Example #13
0
def tablerow(
    token: "Token", parser: "Parser"
) -> Union[nodes.Node, List[nodes.Node]]:
    """The tablerow tag {% tablerow ... %} ... {% endtablerow %}

    Args:
        token: The token matches tag name
        parser: The parser

    Returns:
        The parsed node
    """
    target = parser.parse_assign_target(extra_end_rules=("name:in", ))
    parser.stream.expect("name:in")
    iter_ = parser.parse_tuple(
        with_condexpr=False,
        extra_end_rules=("name:cols", "name:limit", "name:offset"),
    )

    cols = parse_tag_args(parser.stream, "cols", token.lineno)
    limit = parse_tag_args(parser.stream, "limit", token.lineno)
    offset = parse_tag_args(parser.stream, "offset", token.lineno)

    if limit and offset:
        limit = nodes.Add(offset, limit)
    if limit or offset:
        iter_ = nodes.Getitem(iter_, nodes.Slice(offset, limit, None), "load")

    if cols:
        slice_start = nodes.Mul(nodes.Name("_tablerow_i", "load"), cols)
        inner_iter = nodes.Getitem(
            iter_,
            nodes.Slice(
                slice_start,
                nodes.Add(slice_start, cols),
                None,
            ),
            "load",
        )
    else:
        inner_iter: nodes.Getitem = iter_

    inner_body = [
        nodes.Output(
            [
                nodes.Const('<td class="col'),
                nodes.Getattr(nodes.Name("loop", "load"), "index", "load"),
                nodes.Const('">'),
            ]
        ),
        *parser.parse_statements(("name:endtablerow",), drop_needle=True),
        nodes.Output([nodes.Const("</td>")]),
    ]
    tr_begin = nodes.Output(
        [
            nodes.Const('<tr class="row'),
            nodes.CondExpr(
                nodes.Name("loop", "load"),
                nodes.Getattr(nodes.Name("loop", "load"), "index", "load"),
                nodes.Const(1),
            ),
            nodes.Const('">'),
        ]
    )
    tr_end = nodes.Output([nodes.Const("</tr>")])
    inner_loop = nodes.For(
        target, inner_iter, inner_body, [], None, False, lineno=token.lineno
    )
    if not cols:
        return [tr_begin, inner_loop, tr_end]

    # (iter_ | length) / cols
    iter_length = nodes.Div(
        nodes.Filter(iter_, "length", [], [], None, None),
        cols,
    )  # float
    # int(iter_length)
    iter_length_int = nodes.Filter(iter_length, "int", [], [], None, None)

    # implement ceil, as jinja's ceil is implemented as round(..., "ceil")
    # while liquid has a ceil filter
    # iter_length_int if iter_length == iter_length_int
    # else iter_length_int + 1
    iter_length = nodes.CondExpr(
        nodes.Compare(iter_length, [nodes.Operand("eq", iter_length_int)]),
        iter_length_int,
        nodes.Add(iter_length_int, nodes.Const(1)),
    )

    return nodes.For(
        nodes.Name("_tablerow_i", "store"),
        nodes.Call(nodes.Name("range", "load"), [iter_length], [], None, None),
        [tr_begin, inner_loop, tr_end],
        [],
        None,
        False,
        lineno=token.lineno,
    )
Example #14
0

class Builtin(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name


DJEDI_NODE_STORAGE_NODE = (
    nodes.If(
        # if not isinstance(DJEDI_NODE_STORAGE, dict):
        nodes.Not(
            nodes.Call(nodes.Const(Builtin('isinstance')), [
                nodes.Name(DJEDI_NODE_STORAGE, 'load'),
                nodes.Const(Builtin('dict')),
            ], [], None, None)),
        # DJEDI_NODE_STORAGE = {}
        [
            nodes.Assign(nodes.Name(DJEDI_NODE_STORAGE, 'store'), nodes.Dict(
                [])),
        ],
        # else: pass
        []))


class NodeExtension(Extension):
    tags = set([DJEDI_INIT_TAG, DJEDI_TAG, DJEDI_BLOCK_TAG])

    def create_node_id(self, uri, default):
 def output(self, parser, block_call, target, tag_name, lineno):
     if target:
         target_node = nodes.Name(target, 'store', lineno=lineno)
         return nodes.Assign(target_node, block_call, lineno=lineno)
     call = nodes.MarkSafe(block_call, lineno=lineno)
     return nodes.Output([call], lineno=lineno)
Example #16
0
def test_complex():
    title_block = nodes.Block(
        "title", [nodes.Output([nodes.TemplateData("Page Title")])], False)

    render_title_macro = nodes.Macro(
        "render_title",
        [nodes.Name("title", "param")],
        [],
        [
            nodes.Output([
                nodes.TemplateData('\n  <div class="title">\n    <h1>'),
                nodes.Name("title", "load"),
                nodes.TemplateData("</h1>\n    <p>"),
                nodes.Name("subtitle", "load"),
                nodes.TemplateData("</p>\n    "),
            ]),
            nodes.Assign(nodes.Name("subtitle", "store"),
                         nodes.Const("something else")),
            nodes.Output([
                nodes.TemplateData("\n    <p>"),
                nodes.Name("subtitle", "load"),
                nodes.TemplateData("</p>\n  </div>\n"),
                nodes.If(
                    nodes.Name("something", "load"),
                    [
                        nodes.Assign(
                            nodes.Name("title_upper", "store"),
                            nodes.Filter(
                                nodes.Name("title", "load"),
                                "upper",
                                [],
                                [],
                                None,
                                None,
                            ),
                        ),
                        nodes.Output([
                            nodes.Name("title_upper", "load"),
                            nodes.Call(
                                nodes.Name("render_title", "load"),
                                [nodes.Const("Aha")],
                                [],
                                None,
                                None,
                            ),
                        ]),
                    ],
                    [],
                    [],
                ),
            ]),
        ],
    )

    for_loop = nodes.For(
        nodes.Name("item", "store"),
        nodes.Name("seq", "load"),
        [
            nodes.Output([
                nodes.TemplateData("\n    <li>"),
                nodes.Name("item", "load"),
                nodes.TemplateData("</li>\n    <span>"),
            ]),
            nodes.Include(nodes.Const("helper.html"), True, False),
            nodes.Output([nodes.TemplateData("</span>\n  ")]),
        ],
        [],
        None,
        False,
    )

    body_block = nodes.Block(
        "body",
        [
            nodes.Output([
                nodes.TemplateData("\n  "),
                nodes.Call(
                    nodes.Name("render_title", "load"),
                    [nodes.Name("item", "load")],
                    [],
                    None,
                    None,
                ),
                nodes.TemplateData("\n  <ul>\n  "),
            ]),
            for_loop,
            nodes.Output([nodes.TemplateData("\n  </ul>\n")]),
        ],
        False,
    )

    tmpl = nodes.Template([
        nodes.Extends(nodes.Const("layout.html")),
        title_block,
        render_title_macro,
        body_block,
    ])

    tmpl_sym = symbols_for_node(tmpl)
    assert tmpl_sym.refs == {
        "render_title": "l_0_render_title",
    }
    assert tmpl_sym.loads == {
        "l_0_render_title": ("undefined", None),
    }
    assert tmpl_sym.stores == {"render_title"}
    assert tmpl_sym.dump_stores() == {
        "render_title": "l_0_render_title",
    }

    macro_sym = symbols_for_node(render_title_macro, tmpl_sym)
    assert macro_sym.refs == {
        "subtitle": "l_1_subtitle",
        "something": "l_1_something",
        "title": "l_1_title",
        "title_upper": "l_1_title_upper",
    }
    assert macro_sym.loads == {
        "l_1_subtitle": ("resolve", "subtitle"),
        "l_1_something": ("resolve", "something"),
        "l_1_title": ("param", None),
        "l_1_title_upper": ("resolve", "title_upper"),
    }
    assert macro_sym.stores == {"title", "title_upper", "subtitle"}
    assert macro_sym.find_ref("render_title") == "l_0_render_title"
    assert macro_sym.dump_stores() == {
        "title": "l_1_title",
        "title_upper": "l_1_title_upper",
        "subtitle": "l_1_subtitle",
        "render_title": "l_0_render_title",
    }

    body_sym = symbols_for_node(body_block)
    assert body_sym.refs == {
        "item": "l_0_item",
        "seq": "l_0_seq",
        "render_title": "l_0_render_title",
    }
    assert body_sym.loads == {
        "l_0_item": ("resolve", "item"),
        "l_0_seq": ("resolve", "seq"),
        "l_0_render_title": ("resolve", "render_title"),
    }
    assert body_sym.stores == set()

    for_sym = symbols_for_node(for_loop, body_sym)
    assert for_sym.refs == {
        "item": "l_1_item",
    }
    assert for_sym.loads == {
        "l_1_item": ("param", None),
    }
    assert for_sym.stores == {"item"}
    assert for_sym.dump_stores() == {
        "item": "l_1_item",
    }