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