def parse(self, parser): lineno = next(parser.stream).lineno expr = parser.parse_expression() args = [expr] kwargs = [nodes.Keyword('func', expr)] if parser.stream.skip_if('comma'): # Optional 'note' for function docstring if (parser.stream.current.type == 'name' and parser.stream.current.value in ('note', 'cond_for', 'depends_on')): stream_type = parser.stream.current.value next(parser.stream) parser.stream.expect('assign') # Depends meta is always a list if stream_type == 'depends_on': c_expr = parser.parse_list() else: c_expr = parser.parse_expression() args.append(c_expr) kwargs.append(nodes.Keyword(stream_type, c_expr)) body = parser.parse_statements(['name:endsql', 'name:endquery'], drop_needle=True) raw_template = self.environment.sql_params['raws'][parser.name] # Lines range of original raw template raw_lines = slice(lineno, parser.stream.current.lineno - 1) self.environment.sql_params.setdefault('funcs', {}).update( {expr.value: { 'raw_sql': '\n '.join(raw_template[raw_lines]) }}) call_node = nodes.Call(self.attr('_sql_process', lineno=lineno), args, kwargs, None, None) return nodes.CallBlock(call_node, [], [], body)
def parse(self, parser): # the first token is the token that started the tag. In our case # we only listen to ``'script'`` so this will be a name token with # `script` as value. We get the line number so that we can give # that line number to the nodes we create by hand. lineno = next(parser.stream).lineno # Get the current context and pass along kwargs = [nodes.Keyword('ctx', nodes.ContextReference())] # Parse until we are done with optional script tag attributes while parser.stream.current.value in SCRIPT_ATTRS: attr_name = parser.stream.current.value parser.stream.skip(2) kwargs.append( nodes.Keyword(attr_name, parser.parse_expression())) # now we parse the body of the script block up to `endscript` and # drop the needle (which would always be `endscript` in that case) body = parser.parse_statements(['name:endscript'], drop_needle=True) # now return a `CallBlock` node that calls our _render_script # helper method on this extension. return nodes.CallBlock( self.call_method('_render_script', kwargs=kwargs), [], [], body).set_lineno(lineno)
def parse_formrow(self, parser, tag): lineno = tag.lineno field = parser.parse_expression() template_name = None if not parser.stream.current.test('block_end'): template_name = parser.parse_expression() else: template_name = nodes.Call( nodes.Name('get_formrow_template', 'load'), [], [ nodes.Keyword('caller_template', nodes.Const(parser.name)), nodes.Keyword('form', nodes.Name('form_utils_form', 'load')) ], None, None, ) if not parser.stream.current.test('block_end'): raise TemplateSyntaxError("Too many arguments", lineno) node = nodes.Scope(lineno=lineno) assignments = [nodes.Assign(nodes.Name('field', 'store'), field)] node.body = assignments + [ nodes.Include(template_name, True, False) ] return node.set_lineno(lineno)
def parse_trs(self, parser, lineno): lineno = lineno args = [parser.parse_expression()] variables = {} while parser.stream.current.type != 'block_end': parser.stream.expect('comma') 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') kwargs = [] if 'description' in variables: kwargs = [ nodes.Keyword('description', variables.get('description', '')) ] return nodes.Output([ nodes.Call(nodes.Name('translate_trs', 'load'), args, kwargs, None, None) ]).set_lineno(lineno)
def _make_node(self, singular, plural, variables, plural_expr, vars_referenced, num_called_num): """Generates a useful node from the data provided.""" if not vars_referenced and not self.environment.newstyle_gettext: singular = singular.replace('%%', '%') if plural: plural = plural.replace('%%', '%') if plural_expr is None: gettext = nodes.Name('gettext', 'load') node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None) else: ngettext = nodes.Name('ngettext', 'load') node = nodes.Call( ngettext, [nodes.Const(singular), nodes.Const(plural), plural_expr], [], None, None) if self.environment.newstyle_gettext: for key, value in variables.iteritems(): if num_called_num and key == 'num': continue node.kwargs.append(nodes.Keyword(key, value)) else: node = nodes.MarkSafeIfAutoescape(node) if variables: node = nodes.Mod( node, nodes.Dict([ nodes.Pair(nodes.Const(key), value) for key, value in variables.items() ])) return nodes.Output([node])
def parse_args(self, parser): args = [] kwargs = [] require_comma = False target = None while parser.stream.current.type != 'block_end': if parser.stream.current.test('name:as'): parser.stream.skip(1) target = parser.stream.expect('name').value if parser.stream.current.type != 'block_end': parser.fail('Invalid assignment target', parser.stream.current.lineno) break if require_comma: parser.stream.expect('comma') if (parser.stream.current.type == 'name' and parser.stream.look().type == 'assign'): key = parser.stream.current.value parser.stream.skip(2) value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) else: if kwargs: parser.fail('Invalid argument syntax', parser.stream.current.lineno) args.append(parser.parse_expression()) require_comma = True return args, kwargs, target
def parse_args(self, parser): args = [] kwargs = [] require_comma = False while parser.stream.current.type != 'block_end': if require_comma: parser.stream.expect('comma') if parser.stream.current.type == 'name' and parser.stream.look( ).type == 'assign': key = parser.stream.current.value parser.stream.skip(2) value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) else: if kwargs: parser.fail( 'Invalid argument syntax for CacheopsExtension tag', parser.stream.current.lineno) args.append(parser.parse_expression()) require_comma = True return args, kwargs
def parse(self, parser): node = nodes.ScopedEvalContextModifier( lineno=next(parser.stream).lineno) node.options = [nodes.Keyword('autoescape', parser.parse_expression())] node.body = parser.parse_statements(('name:endautoescape', ), drop_needle=True) return nodes.Scope([node])
def parse(self, parser): '''parse content of extension''' # line number of token that started the tag lineno = next(parser.stream).lineno # template context context = nodes.ContextReference() # parse keyword arguments kwargs = [] while parser.stream.look().type == lexer.TOKEN_ASSIGN: key = parser.stream.expect(lexer.TOKEN_NAME) next(parser.stream) kwargs.append(nodes.Keyword(key.value, parser.parse_expression()), ) parser.stream.skip_if('comma') # parse content of the activeurl block up to endactiveurl body = parser.parse_statements(['name:endactiveurl'], drop_needle=True) args = [context] call_method = self.call_method( 'render_tag', args=args, kwargs=kwargs, ) return nodes.CallBlock(call_method, [], [], body).set_lineno(lineno)
def parse_tropts(self, parser, lineno): """ {% tropts source="index" %} {% tr %} {% endtr %} {% tr %} {% endtr %} {% tr %} {% endtr %} {% endtropts %} """ node = nodes.Scope(lineno=lineno) assignments = [] while parser.stream.current.type != 'block_end': lineno = parser.stream.current.lineno if assignments: parser.stream.expect('comma') key = parser.parse_assign_target() # a=b (a is key) parser.stream.expect('assign') value = parser.parse_expression() # a=b (b is expression) assignments.append(nodes.Keyword(key.name, value)) node.body = parser.parse_statements(('name:endtropts',), drop_needle=True) for item in node.body: if isinstance(item, (nodes.Call, nodes.CallBlock)) and item.call.node.name in ('_translate_trs', '_translate_tr'): used_keys = set(arg.key for arg in item.call.args) allowed_assignments = [assign for assign in assignments if not assign.key in used_keys] item.call.args += allowed_assignments return node
def parse(self, parser): lineno = parser.stream.current.lineno tag_name = parser.stream.current.value additional_params = [ nodes.Keyword('_context', nodes.ContextReference()), nodes.Keyword('_template', nodes.Const(parser.name)), nodes.Keyword('_lineno', nodes.Const(lineno)), nodes.Keyword('_tag_name', nodes.Const(tag_name)), ] self.init_parser(parser) args, kwargs, target = self.parse_args(parser) kwargs.extend(additional_params) block_call = self.call_method('render_wrapper', args, kwargs) return self.output(parser, block_call, target, tag_name=tag_name, lineno=lineno)
def _make_node( self, singular, plural, variables, plural_expr, vars_referenced, num_called_num ): """Generates a useful node from the data provided.""" # no variables referenced? no need to escape for old style # gettext invocations only if there are vars. if not vars_referenced and not self.environment.newstyle_gettext: singular = singular.replace("%%", "%") if plural: plural = plural.replace("%%", "%") # singular only: if plural_expr is None: gettext = nodes.Name("gettext", "load") node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None) # singular and plural else: ngettext = nodes.Name("ngettext", "load") node = nodes.Call( ngettext, [nodes.Const(singular), nodes.Const(plural), plural_expr], [], None, None, ) # in case newstyle gettext is used, the method is powerful # enough to handle the variable expansion and autoescape # handling itself if self.environment.newstyle_gettext: for key, value in iteritems(variables): # the function adds that later anyways in case num was # called num, so just skip it. if num_called_num and key == "num": continue node.kwargs.append(nodes.Keyword(key, value)) # otherwise do that here else: # mark the return value as safe if we are in an # environment with autoescaping turned on node = nodes.MarkSafeIfAutoescape(node) if variables: node = nodes.Mod( node, nodes.Dict( [ nodes.Pair(nodes.Const(key), value) for key, value in variables.items() ] ), ) return nodes.Output([node])
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 _get_named_params(self, expressions): '''This method transform a list of expressions into a dictionary. It is mandatory that the given list has even number of expressions otherwise an exception is raised. :param expressions: A list of jinja2 expressions. :type expressions: list :returns: A tuple (list of kwargs nodes, dict of missing_arguments).''' named_params = [] missing_params = { Component.COMP_ARG_TEMPLATE: True, Component.COMP_ARG_URL: True, Component.COMP_ARG_RUNTIME: True } last_lineno = -1 expr_iterator = iter(expressions) for expr_name in expr_iterator: expr_value = next(expr_iterator) missing_params[expr_name.name] = False last_lineno = expr_value.lineno named_params.append( nodes.Keyword(expr_name.name, expr_value, lineno=last_lineno)) if missing_params[Component.COMP_ARG_TEMPLATE]: named_params.insert( 0, nodes.Keyword(Component.COMP_ARG_TEMPLATE, Const(Component.COMP_TEMPLATE_DEFAULT), lineno=last_lineno)) if missing_params[Component.COMP_ARG_RUNTIME]: named_params.append( nodes.Keyword(Component.COMP_ARG_RUNTIME, Const(Component.COMP_RUNTIME_DEFAULT), lineno=last_lineno)) return named_params, missing_params
def parse_call(self, node): token = self.stream.expect('lparen') args = [] kwargs = [] dyn_args = dyn_kwargs = None require_comma = False def ensure(expr): if not expr: self.fail('invalid syntax for function call expression', token.lineno) while self.stream.current.type != 'rparen': if require_comma: self.stream.expect('comma') # support for trailing comma if self.stream.current.type == 'rparen': break if self.stream.current.type == 'mul': ensure(dyn_args is None and dyn_kwargs is None) next(self.stream) dyn_args = self.parse_expression() elif self.stream.current.type == 'pow': ensure(dyn_kwargs is None) next(self.stream) dyn_kwargs = self.parse_expression() else: if self.stream.current.type == 'name' and \ self.stream.look().type == 'assign': # Parsing a kwarg ensure(dyn_kwargs is None) key = self.stream.current.value self.stream.skip(2) value = self.parse_expression() kwargs.append( nodes.Keyword(key, value, lineno=value.lineno)) else: # Parsing an arg ensure(dyn_args is None and dyn_kwargs is None and not kwargs) args.append(self.parse_expression()) require_comma = True self.stream.expect('rparen') if node is None: return args, kwargs, dyn_args, dyn_kwargs return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
def parse_form(self, parser, tag): lineno = tag.lineno form_instance = parser.parse_expression() template_name = nodes.Call( nodes.Name('get_formlayout_template', 'load'), [], [ nodes.Keyword('caller_template', nodes.Const(parser.name)), nodes.Keyword('form', form_instance), ], None, None, ) has_body = False if not parser.stream.current.test('block_end'): parser.stream.expect('name:using') if parser.stream.current.test('block_end'): has_body = True if not parser.stream.current.test('block_end'): template_name = parser.parse_expression() if not parser.stream.current.test('block_end'): raise TemplateSyntaxError("Too many arguments", lineno) body = None if has_body: body = parser.parse_statements(['name:endform'], drop_needle=True) else: body = nodes.Include(template_name, True, False) body = [body] node = nodes.Scope(lineno=lineno) assignments = [ nodes.Assign(nodes.Name('form_utils_form', 'store'), form_instance) ] node.body = assignments + body return node.set_lineno(lineno)
def parse_call(self, node): token = self.stream.expect("lparen") args = [] kwargs = [] dyn_args = dyn_kwargs = None require_comma = False def ensure(expr): if not expr: self.fail("invalid syntax for function call expression", token.lineno) while self.stream.current.type != "rparen": if require_comma: self.stream.expect("comma") # support for trailing comma if self.stream.current.type == "rparen": break if self.stream.current.type == "mul": ensure(dyn_args is None and dyn_kwargs is None) next(self.stream) dyn_args = self.parse_expression() elif self.stream.current.type == "pow": ensure(dyn_kwargs is None) next(self.stream) dyn_kwargs = self.parse_expression() else: ensure(dyn_args is None and dyn_kwargs is None) if (self.stream.current.type == "name" and self.stream.look().type == "assign"): key = self.stream.current.value self.stream.skip(2) value = self.parse_expression() kwargs.append( nodes.Keyword(key, value, lineno=value.lineno)) else: ensure(not kwargs) args.append(self.parse_expression()) require_comma = True self.stream.expect("rparen") if node is None: return args, kwargs, dyn_args, dyn_kwargs return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
def parse_tr(self, parser, lineno): node = nodes.Scope(lineno=lineno) assignments = [] while parser.stream.current.type != 'block_end': lineno = parser.stream.current.lineno if assignments: parser.stream.expect('comma') target = parser.parse_assign_target(name_only=True) parser.stream.expect('assign') expr = parser.parse_expression() assignments.append(nodes.Keyword(target.name, expr, lineno=lineno)) body = parser.parse_statements(('name:endtr', ), drop_needle=True) return nodes.CallBlock( nodes.Call(nodes.Name('translate_tr', 'load'), [], assignments, None, None), [], [], body).set_lineno(lineno)
def parse_macro_tag_signature(parser): args = [] kwargs = [] while parser.stream.current.type != "block_end": if parser.stream.current.type == "name" and \ parser.stream.look().type == "assign": key = parser.stream.current.value parser.stream.skip(2) value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) else: args.append(parser.parse_expression()) return args, kwargs
def parse(self, parser): lineno = next(parser.stream).lineno view_name = parser.stream.expect(lexer.TOKEN_STRING) view_name = nodes.Const(view_name.value, lineno=view_name.lineno) args = None kwargs = None as_var = None while parser.stream.current.type != lexer.TOKEN_BLOCK_END: token = parser.stream.current if token.test("name:as"): next(parser.stream) token = parser.stream.expect(lexer.TOKEN_NAME) as_var = nodes.Name(token.value, "store", lineno=token.lineno) break if args is not None: args.append(self.parse_expression(parser)) elif kwargs is not None: if token.type != lexer.TOKEN_NAME: parser.fail( "got '{}', expected name for keyword argument" "".format(lexer.describe_token(token)), lineno=token.lineno, ) arg = token.value next(parser.stream) parser.stream.expect(lexer.TOKEN_ASSIGN) token = parser.stream.current kwargs[arg] = self.parse_expression(parser) else: if parser.stream.look().type == lexer.TOKEN_ASSIGN: kwargs = {} else: args = [] continue if args is None: args = [] args.insert(0, view_name) if kwargs is not None: kwargs = [nodes.Keyword(key, val) for key, val in kwargs.items()] call = self.call_method("_url_reverse", args, kwargs, lineno=lineno) if as_var is None: return nodes.Output([call], lineno=lineno) else: return nodes.Assign(as_var, call, lineno=lineno)
def _parse_kwargs(self, parser): lineno = next(parser.stream).lineno # XXX Is env required? #env = nodes.Name() #env.name = 'env' #env.ctx = 'load' #kwargs = [nodes.Keyword('env', env)] kwargs = [] while parser.stream.current.type != 'block_end': if len(kwargs) > 1: parser.stream.expect('comma') key = parser.stream.expect('name').value parser.stream.expect('assign') value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value)) return kwargs
def parse(self, parser): lineno = parser.stream.next().lineno object_list = parser.parse_expression() if parser.stream.skip_if('name:as'): name = parser.stream.expect('name').value elif hasattr(object_list, 'name'): name = object_list.name else: raise TemplateSyntaxError( "Cannot determine the name of objects " \ "you want to paginate, use 'as foobar' syntax", lineno) kwargs = [] # wait... what? loops = 0 while parser.stream.current.type != 'block_end': lineno = parser.stream.current.lineno if loops: parser.stream.expect('comma') key = parser.parse_assign_target().name if key not in self.default_kwargs.keys(): raise TemplateSyntaxError( "Unknown keyword argument for autopaginate. "\ "Your options are: %s" % ( ", ".join(self.default_kwargs.keys()) )) parser.stream.expect('assign') value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value)) loops += 1 return [ nodes.Assign(nodes.Name(name + '_pages', 'store'), self.call_method('_render_pages', [object_list, nodes.Name('request', 'load')], kwargs) ).set_lineno(lineno), nodes.Assign(nodes.Name(name, 'store'), nodes.Getattr(nodes.Name(name + '_pages', 'load'), 'object_list', nodes.Impossible()) ).set_lineno(lineno), ]
def _empty_extension_parse(self, open_token_condition, parser): args = [ nodes.ContextReference(), nodes.Const(parser.filename), nodes.Const(parser.stream.current.lineno) ] kwargs = {} name_token = parser.stream.expect(open_token_condition) automata_state = AutomataState.Expect_Name while parser.stream.current.type != TOKEN_BLOCK_END: if automata_state == AutomataState.Expect_Name: name_token = parser.stream.expect(TOKEN_NAME) automata_state = AutomataState.Expect_Assign elif automata_state == AutomataState.Expect_Assign: parser.stream.skip_if(TOKEN_ASSIGN) automata_state = AutomataState.Expect_Value elif automata_state == AutomataState.Expect_Value: value_token = parser.stream.next_if(TOKEN_FLOAT) if value_token: kwargs[name_token.value] = value_token.value else: value_token = parser.stream.next_if(TOKEN_INTEGER) if value_token: kwargs[name_token.value] = value_token.value else: value_token = parser.stream.expect(TOKEN_STRING) kwargs[name_token.value] = value_token.value automata_state = AutomataState.Expect_Comma elif automata_state == AutomataState.Expect_Comma: parser.stream.skip_if(TOKEN_COMMA) automata_state = AutomataState.Expect_Name lineno = parser.stream.current.lineno return nodes.CallBlock( self.call_method('_process_markup', args, [ nodes.Keyword(name, nodes.Const(value)) for name, value in kwargs.items() ]), [], [], []).set_lineno(lineno)
def cycle(token: "Token", parser: "Parser") -> nodes.Node: """The cycle tag {% cycle ... %} With name: {% cycle "name": "one", "two", "three" %} Without: {% cycle "one", "two", "three" %} Turn these to {{ loop.liquid_cycle("one", "two", "three", name=...) }} Args: token: The token matches tag name parser: The parser Returns: The parsed node """ tokens_ahead = peek_tokens(parser.stream, 2) if ( len(tokens_ahead) == 2 and tokens_ahead[0].type is TOKEN_STRING and tokens_ahead[1].type is TOKEN_COLON ): parser.stream.skip(2) cycler_name = tokens_ahead[0].value else: cycler_name = "" args = parser.parse_tuple(with_condexpr=False, simplified=True) return nodes.Output( [ nodes.Call( nodes.Getattr( nodes.Name("loop", "load"), "liquid_cycle", "load" ), args.items if isinstance(args, nodes.Tuple) else [args], [nodes.Keyword("name", nodes.Const(cycler_name))], None, None, lineno=token.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_args(self, parser): args = [] kwargs = [] require_comma = False while parser.stream.current.type != "block_end": if require_comma: parser.stream.expect("comma") if (parser.stream.current.type == "name" and parser.stream.look().type == "assign"): key = parser.stream.current.value parser.stream.skip(2) value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) else: if kwargs: parser.fail( "Invalid argument syntax for WrapExtension tag", parser.stream.current.lineno, ) args.append(parser.parse_expression()) require_comma = True return args, kwargs
def parse(self, parser): token = parser.stream.next() args = [parser.parse_expression()] kwargs = [] while parser.stream.current.type != 'block_end': if kwargs: parser.stream.expect('comma') if parser.stream.skip_if('colon'): break name = parser.stream.expect('name') if name.value in kwargs: parser.fail('variable %r defined twice.' % name.value, name.lineno, exc=TemplateAssertionError) parser.stream.expect('assign') key = name.value value = parser.parse_expression() kwargs.append(nodes.Keyword(key, value, lineno=value.lineno)) return nodes.Output([self.call_method('_render', args, kwargs) ]).set_lineno(token.lineno)
def parse(self, parser): stream = parser.stream tag = stream.next() # 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 = stream.next() 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(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()) 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)
def _parse_blocktrans(self, parser, lineno): with_vars = {} count = None context = None trimmed = False as_var = None if parser.stream.skip_if('name:trimmed'): trimmed = True if parser.stream.skip_if('name:asvar'): as_var = parser.stream.expect(lexer.TOKEN_NAME) as_var = nodes.Name(as_var.value, 'store', lineno=as_var.lineno) if parser.stream.skip_if('name:with'): while parser.stream.look().type == lexer.TOKEN_ASSIGN: token = parser.stream.expect(lexer.TOKEN_NAME) key = token.value next(parser.stream) with_vars[key] = parser.parse_expression(False) if parser.stream.skip_if('name:count'): name = parser.stream.expect(lexer.TOKEN_NAME).value parser.stream.expect(lexer.TOKEN_ASSIGN) value = parser.parse_expression(False) count = (name, value) if parser.stream.skip_if('name:context'): context = parser.stream.expect(lexer.TOKEN_STRING).value parser.stream.expect(lexer.TOKEN_BLOCK_END) body_singular = None body = [] additional_vars = set() for token in parser.stream: if token is lexer.TOKEN_EOF: parser.fail( 'unexpected end of template, expected endblocktrans tag') if token.type is lexer.TOKEN_DATA: body.append(token.value) elif token.type is lexer.TOKEN_VARIABLE_BEGIN: name = parser.stream.expect(lexer.TOKEN_NAME).value if name not in with_vars and (count is None or count[0] != name): additional_vars.add(name) parser.stream.expect(lexer.TOKEN_VARIABLE_END) # django converts variables inside the blocktrans tag into # "%(var_name)s" format, so we do the same. body.append('%({})s'.format(name)) elif token.type is lexer.TOKEN_BLOCK_BEGIN: if body_singular is None and parser.stream.skip_if( 'name:plural'): if count is None: parser.fail('used plural without specifying count') parser.stream.expect(lexer.TOKEN_BLOCK_END) body_singular = body body = [] else: parser.stream.expect('name:endblocktrans') break if count is not None and body_singular is None: parser.fail('plural form not found') trans_vars = [ nodes.Pair(nodes.Const(key), val, lineno=lineno) for key, val in with_vars.items() ] if count is not None: trans_vars.append( nodes.Pair(nodes.Const(count[0]), count[1], lineno=lineno)) trans_vars.extend( nodes.Pair(nodes.Const(key), nodes.Name(key, 'load', lineno=lineno), lineno=lineno) for key in additional_vars) kwargs = [ nodes.Keyword('trans_vars', nodes.Dict(trans_vars, lineno=lineno), lineno=lineno) ] if context is not None: kwargs.append( nodes.Keyword('context', nodes.Const(context, lineno=lineno), lineno=lineno)) if count is not None: kwargs.append( nodes.Keyword('count_var', nodes.Const(count[0], lineno=lineno), lineno=lineno)) body = ''.join(body) if trimmed: body = ' '.join(map(lambda s: s.strip(), body.strip().splitlines())) if body_singular is not None: body_singular = ''.join(body_singular) if trimmed: body_singular = ' '.join( map(lambda s: s.strip(), body_singular.strip().splitlines())) if body_singular is None: args = [] else: args = [nodes.TemplateData(body_singular, lineno=lineno)] args.append(nodes.TemplateData(body, lineno=lineno)) call = nodes.MarkSafe(self.call_method('_make_blocktrans', args, kwargs), lineno=lineno) if as_var is None: return nodes.Output([call], lineno=lineno) else: return nodes.Assign(as_var, call)
def _embed_extension_parse(self, open_token_condition, close_token_condition, parser): args = [ nodes.ContextReference(), nodes.Const(parser.filename), nodes.Const(parser.stream.current.lineno) ] kwargs = {} name_token = parser.stream.expect(open_token_condition) automata_state = AutomataState.Expect_Name content_path = None while parser.stream.current.type != TOKEN_BLOCK_END: if automata_state == AutomataState.Expect_Name: name_token = parser.stream.expect(TOKEN_NAME) automata_state = AutomataState.Expect_Assign elif automata_state == AutomataState.Expect_Assign: parser.stream.skip_if(TOKEN_ASSIGN) automata_state = AutomataState.Expect_Value elif automata_state == AutomataState.Expect_Value: value_token = parser.stream.next_if(TOKEN_FLOAT) if value_token: kwargs[name_token.value] = value_token.value else: value_token = parser.stream.next_if(TOKEN_INTEGER) if value_token: kwargs[name_token.value] = value_token.value else: value_token = parser.stream.expect(TOKEN_STRING) if name_token.value == 'absolute_path': content_path = normpath( abspath( join(parser.environment.globals['source_path'], value_token.value))) elif name_token.value == 'relative_path': content_path = normpath( abspath( join(dirname(parser.filename), value_token.value))) else: kwargs[name_token.value] = value_token.value automata_state = AutomataState.Expect_Comma elif automata_state == AutomataState.Expect_Comma: parser.stream.skip_if(TOKEN_COMMA) automata_state = AutomataState.Expect_Name lineno = parser.stream.current.lineno if content_path is not None: if not isfile(content_path): raise TemplateSyntaxError( f'Cannot find content file "{content_path}".', lineno, parser.filename) kwargs['content_path'] = content_path body = [] else: body = parser.parse_statements([close_token_condition], drop_needle=True) return nodes.CallBlock( self.call_method('_process_markup', args, [ nodes.Keyword(name, nodes.Const(value)) for name, value in kwargs.items() ]), [], [], body).set_lineno(lineno)