def make_call_node(*kw):
     return self.call_method(
         "_call",
         args=[
             nodes.List(args),
             nodes.Dict(kwargs),
         ],
         kwargs=kw,
     )
示例#2
0
 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)
示例#3
0
 def parse_list(self):
     token = self.stream.expect("lbracket")
     items = []
     while self.stream.current.type != "rbracket":
         if items:
             self.stream.expect("comma")
         if self.stream.current.type == "rbracket":
             break
         items.append(self.parse_expression())
     self.stream.expect("rbracket")
     return nodes.List(items, lineno=token.lineno)
示例#4
0
    def parse(self, parser):
        stream = parser.stream

        tag = stream.next()

        # get view name
        if stream.current.test('string'):
            viewname = parser.parse_primary()
        else:
            # parse valid tokens and manually build a string from them
            bits = []
            name_allowed = True
            while True:
                if stream.current.test_any('dot', 'sub'):
                    bits.append(stream.next())
                    name_allowed = True
                elif stream.current.test('name') and name_allowed:
                    bits.append(stream.next())
                    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(stream.next().value)
                stream.skip()
                value = parser.parse_expression()
                kwargs.append(nodes.Pair(key, value, lineno=key.lineno))
            else:
                args.append(parser.parse_expression())

        make_call_node = lambda *kw: \
            self.call_method('_reverse',
                             args=[viewname, nodes.List(args), nodes.Dict(kwargs)],
                             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)
示例#5
0
 def parse_list(self):
     token = self.stream.expect('lbracket')
     items = []
     while self.stream.current.type != 'rbracket':
         if items:
             self.stream.expect('comma')
         if self.stream.current.type == 'rbracket':
             break
         items.append(self.parse_expression())
     self.stream.expect('rbracket')
     return nodes.List(items, lineno=token.lineno)
示例#6
0
    def create_node_storage(self):
        # Keep reference to allow adding nodes during parsing
        self._node_storage = []

        create_nodes = nodes.Assign(
            nodes.Name('__', 'store'),
            self.call_method('_create_nodes', [
                nodes.Name(DJEDI_NODE_STORAGE, 'load'),
                nodes.List(self._node_storage),
            ]))
        return [DJEDI_NODE_STORAGE_NODE, create_nodes]
示例#7
0
    def parse(self, parser):
        lineno = next(parser.stream).lineno
        variable_names = []
        while parser.stream.current.type != 'block_end':
            variable_names.append(next(parser.stream))

        node = nodes.CallBlock(
            self.call_method('_require', [
                nodes.List(
                    [nodes.Const(token.value) for token in variable_names])
            ]), [], [], []).set_lineno(lineno)
        return parser.parse_import_context(node, True)
    def parse(self, parser):
        lineno = parser.stream.next().lineno

        expire_time = parser.parse_expression()
        fragment_name = parser.parse_expression()
        vary_on = []
        while not parser.stream.current.test('block_end'):
            vary_on.append(parser.parse_expression())

        body = parser.parse_statements(['name:endcache'], drop_needle=True)

        return nodes.CallBlock(
            self.call_method('_cache_support',
                             [expire_time, fragment_name,
                              nodes.List(vary_on), nodes.Const(lineno)]),
            [], [], body).set_lineno(lineno)
示例#9
0
    def parse_experiment_enroll(self, parser):
        """Parse {% experiment_enroll ... %} tags"""

        lineno = parser.stream.current.lineno

        # list of nodes that will be used when calling the callback:
        args = []

        # parsing first parameter:
        experiment_name = parser.stream.current
        args.append(self._name_or_const(experiment_name))
        next(parser.stream)

        # parsing remaining parameters (the "alternatives"):
        alternatives = []
        while parser.stream.current.type != 'block_end':
            if self._token_as(parser):
                break
            alternatives.append(self._name_or_const(parser.stream.current))
            next(parser.stream)
        args.append(nodes.List(alternatives))

        # expecting `as` after the alternatives:
        if not self._token_as(parser):
            raise TemplateSyntaxError(
                'Syntax should be like: '
                '{% experiment_enroll "experiment_name"'
                ' "alternative1" "alternative2" ... as some_variable %}',
                lineno,
            )
        next(parser.stream)

        # parse what comes after `as`:
        target = parser.parse_assign_target()

        # We're done with parsing the tag.

        # we will also need the context in the callback:
        args.append(nodes.ContextReference())

        # create a callback node that will be executed on render:
        call_node = self.call_method('render_experiment_enroll',
                                     args,
                                     lineno=lineno)

        # return an assignment node that will trigger the callback:
        return nodes.Assign(target, call_node, lineno=lineno)
示例#10
0
    def parse(self, parser):
        lineno = next(parser.stream).lineno

        viewlet_args = []
        name = None
        first = True
        while parser.stream.current.type != 'block_end':
            if not first:
                parser.stream.expect('comma')
                viewlet_args.append(parser.parse_expression())
            else:
                name = parser.parse_expression()
            first = False
        context = nodes.ContextReference()
        return nodes.CallBlock(
            self.call_method('_call_viewlet', args=[name, context, nodes.List(viewlet_args)]),
            [], [], []).set_lineno(lineno)
示例#11
0
    def parse(self, parser):
        lineno = parser.stream.next().lineno

        files = []
        output = nodes.Const(None)
        filters = 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 = parser.stream.next().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, DeprecationWarning)
                elif name == 'output':
                    output = 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:
                files.append(parser.parse_expression())

        # parse the contents of this tag, and return a block
        body = parser.parse_statements(['name:endassets'], drop_needle=True)
        return nodes.CallBlock(
                self.call_method('_render_assets',
                                 args=[filters, output, nodes.List(files)]),
                [nodes.Name('ASSET_URL', 'store')], [], body).\
                    set_lineno(lineno)
示例#12
0
 def _parse_profile_block(self, parser, label, source, body, lineno):
     profile_id = self._create_profile_id(parser)
     ret = ([
         nodes.Assign(
             nodes.Name(profile_id, "store").set_lineno(lineno),
             self.call_method(
                 "_profile_start",
                 dyn_args=nodes.List([label, nodes.Const(source)
                                      ]).set_lineno(lineno),
             ).set_lineno(lineno),
         ).set_lineno(lineno),
     ] + body + [
         nodes.ExprStmt(
             self.call_method(
                 "_profile_end", dyn_args=nodes.Name(
                     profile_id, "load")), ).set_lineno(lineno),
     ])
     return ret
示例#13
0
    def parse(self, parser):
        """Parse the bundles block and feed the bundles environment.

        Bundles entries are replaced by an empty string.
        """
        lineno = next(parser.stream).lineno

        bundles = []
        while parser.stream.current.type != "block_end":
            value = parser.parse_expression()
            bundles.append(value)
            parser.stream.skip_if("comma")

        call = self.call_method("_update", args=[nodes.Const(parser.name),
                                                 nodes.List(bundles)])
        call_block = nodes.CallBlock(call, [], [], '')
        call_block.set_lineno(lineno)
        return call_block
示例#14
0
    def parse(self, parser):
        lineno = next(parser.stream).lineno

        args = [parser.parse_expression()]  # 解析标签参数,包括 timeout
        if parser.stream.skip_if('comma'):
            args.append(parser.parse_expression())
        else:
            args.append(nodes.Const('%s%s' % (parser.filename, lineno)))
        vary_on = []
        while parser.stream.skip_if('comma'):
            vary_on.append(parser.parse_expression())

        if vary_on:
            args.append(nodes.List(vary_on))
        else:
            args.append(nodes.Const([]))

        body = parser.parse_statements(['name:endcache'], drop_needle=True)
        return nodes.CallBlock(self.call_method('_cache', args), [], [],
                               body).set_lineno(lineno)
示例#15
0
    def parse(self, parser):
        stream = parser.stream

        tag = stream.next()

        # get view name
        if stream.current.test('string'):
            bundle_name = parser.parse_primary()
        else:
            bundle_name = parser.parse_expression()

        # 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(stream.next().value)
                stream.skip()
                value = parser.parse_expression()
                kwargs.append(nodes.Pair(key, value, lineno=key.lineno))
            else:
                args.append(parser.parse_expression())

        make_call_node = lambda *kw: \
            self.call_method('_build_tag',
                             args=[bundle_name,
                             nodes.List(args), nodes.Dict(kwargs)],
                             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)
示例#16
0
    def parse(self, parser):
        lineno = parser.stream.next().lineno

        # Parse timeout
        args = [parser.parse_expression()]

        # Parse fragment name
        parser.stream.skip_if('comma')
        args.append(parser.parse_expression())

        # Parse vary_on parameters
        vary_on = []
        while parser.stream.skip_if('comma'):
            vary_on.append(parser.parse_expression())

        if vary_on:
            args.append(nodes.List(vary_on))
        else:
            args.append(nodes.Const([]))

        body = parser.parse_statements(['name:endcache'], drop_needle=True)
        return nodes.CallBlock(self.call_method('_cache', args), [], [],
                               body).set_lineno(lineno)
示例#17
0
    def parse(self, parser):
        lineno = next(parser.stream).lineno

        args = [
            parser.parse_expression(),  # Timeout
            parser.parse_expression(),  # Domain
            parser.parse_expression(),  # Path
            nodes.Const("%s%s" % (parser.filename, lineno))
        ]

        #: Parse vary_on parameters
        vary_on = []
        while parser.stream.skip_if('comma'):
            vary_on.append(parser.parse_expression())

        if vary_on:
            args.append(nodes.List(vary_on))
        else:
            args.append(nodes.Const([]))

        body = parser.parse_statements(['name:enddomaincache'],
                                       drop_needle=True)
        return nodes.CallBlock(self.call_method('_cache', args), [], [],
                               body).set_lineno(lineno)
示例#18
0
    def parse(self, parser):
        lineno = parser.stream.next().lineno
        #cache key name is "template file path" + "line no"
        default_cache_key_name = u"%s%s" % (parser.filename, lineno)
        default_cache_key_name.encode('utf-8')

        cache_key_names = [nodes.Const(default_cache_key_name)]
        #parse timeout
        if parser.stream.current.type != 'block_end':
            timeout = parser.parse_expression()
            while parser.stream.skip_if('comma'):
                keyname = parser.parse_expression()
                if isinstance(keyname, nodes.Name):
                    keyname = nodes.Const(keyname.name)
                cache_key_names.append(keyname)
        else:
            timeout = nodes.Const(None)

        args = [nodes.List(cache_key_names), timeout]

        body = parser.parse_statements(['name:endcache'], drop_needle=True)

        return nodes.CallBlock(self.call_method('_cache', args),
            [], [], body).set_lineno(lineno)
示例#19
0
    def parse(self, parser):
        lineno = parser.stream.next().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 = parser.stream.next().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:
                files.append(parser.parse_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', 'store'),
                nodes.Name('EXTRA', 'store')]

        # 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
示例#20
0
 def _selected_templates(self, templates, prefixes):
     return nodes.List([
         nodes.Add(prefix, template) for template in templates.items
         for prefix in prefixes.items
     ])
示例#21
0
 def make_call_node(*kw: Any) -> Any:
     return self.call_method('_call', args=[
         nodes.List(args),
         nodes.Dict(kwargs),
     ], kwargs=list(kw))