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 _make_node(self, singular, plural, variables, plural_expr): """Generates a useful node from the data provided.""" # 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) # mark the return value as safe if we are in an # environment with autoescaping turned on if self.environment.autoescape: node = nodes.MarkSafe(node) if variables: node = nodes.Mod(node, variables) return nodes.Output([node])
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): 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): node = nodes.ExprStmt(lineno=next(parser.stream).lineno) modules = [] while parser.stream.current.type != 'block_end': lineno = parser.stream.current.lineno if modules: parser.stream.expect('comma') expr = parser.parse_expression() module = expr.as_const() modules.append(module) assignments = [] from djinja.template.defaultfunctions import Load for m in modules: target = nodes.Name(m, 'store') func = nodes.Call(nodes.Name('load', 'load'), [nodes.Const(m)], [], None, None) assignments.append(nodes.Assign(target, func, lineno=lineno)) for i in Load(m).globals.keys(): target = nodes.Name(i, 'store') f = nodes.Getattr(nodes.Name(m, 'load'), i, 'load') assignments.append(nodes.Assign(target, f, lineno=lineno)) return assignments
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_part(self, parser, token): node = nodes.Macro(lineno=token.lineno) part_name = parser.stream.expect('name').value node.name = "compopart_" + part_name parser.parse_signature(node) node.args = [ nodes.Name("self", "param"), nodes.Name("has_caller", "param") ] + node.args # + [nodes.Name("caller", "param")] assign_node = nodes.Assign() assign_node.target = nodes.Name("dummy", "store") call_node = nodes.Call() call_node.node = nodes.Getattr() call_node.node.node = nodes.Name("self", "load") call_node.node.attr = "overwrite_compopart" # calls the method "self.overwrite_compopart()" from the components call_node.node.ctx = "load" call_node.args = [ nodes.Const(part_name), nodes.Name('compopart_' + part_name, 'load') ] call_node.kwargs = [] call_node.dyn_args = None call_node.dyn_kwargs = None assign_node.node = call_node node.body = parser.parse_statements(('name:endcompopart', ), drop_needle=True) return [node, assign_node]
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 parse_load(self, parser): filter_name = parser.stream.current.value lineno = next(parser.stream).lineno if filter_name not in self.environment.filters: parser.fail("Unable to parse {0}".format(filter_name), lineno) parser.stream.expect("name:as") target = parser.parse_assign_target() macro_name = "_" + parser.free_identifier().name macro_body = parser.parse_statements(("name:endload", ), drop_needle=True) return [ nodes.Macro(macro_name, [], [], macro_body).set_lineno(lineno), nodes.Assign( target, nodes.Filter( nodes.Call( nodes.Name(macro_name, "load").set_lineno(lineno), [], [], None, None, ).set_lineno(lineno), filter_name, [], [], None, None, ).set_lineno(lineno), ).set_lineno(lineno), ]
def parse(self, parser): lineno = next(parser.stream).lineno index = nodes.Name("_", "store", lineno=lineno) how_many_times = parser.parse_expression() iterable = nodes.Call(nodes.Name("range", "load"), [how_many_times], [], None, None) parser.stream.expect("name:times") body = parser.parse_statements(["name:endrepeat"], drop_needle=True) return nodes.For(index, iterable, body, [], None, False, lineno=lineno)
def _parse_trans(self, parser, lineno): string = parser.stream.expect(lexer.TOKEN_STRING) string = nodes.Const(string.value, lineno=string.lineno) is_noop = False context = None as_var = None for token in iter(lambda: parser.stream.next_if(lexer.TOKEN_NAME), None): if token.value == 'noop' and not is_noop: if context is not None: parser.fail("noop translation can't have context", lineno=token.lineno) is_noop = True elif token.value == 'context' and context is None: if is_noop: parser.fail("noop translation can't have context", lineno=token.lineno) context = parser.stream.expect(lexer.TOKEN_STRING) context = nodes.Const(context.value, lineno=context.lineno) elif token.value == 'as' and as_var is None: as_var = parser.stream.expect(lexer.TOKEN_NAME) as_var = nodes.Name(as_var.value, 'store', lineno=as_var.lineno) else: parser.fail("expected 'noop', 'context' or 'as'", lineno=token.lineno) if is_noop: output = string elif context is not None: func = nodes.Name('pgettext', 'load', lineno=lineno) output = nodes.Call(func, [context, string], [], None, None, lineno=lineno) else: func = nodes.Name('gettext', 'load') output = nodes.Call(func, [string], [], None, None, lineno=lineno) if as_var is None: return nodes.Output([output], lineno=lineno) else: return nodes.Assign(as_var, output, lineno=lineno)
def call_method(self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None): """Call a method of the extension. This is a shortcut for :meth:`attr` + :class:`jinja2.nodes.Call`. """ if args is None: args = [] if kwargs is None: kwargs = [] return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, dyn_args, dyn_kwargs, lineno=lineno)
def parse(self, parser): is_block = parser.stream.current.test("name:call_macro_tag") lineno = parser.stream.__next__().lineno macro_name = self.environment.macros.resolve_alias(parser.stream.current.value) parser.stream.__next__() args, kwargs = parse_macro_tag_signature(parser) call = nodes.Call(nodes.Name(macro_name, "load"), args, kwargs, None, None, lineno=lineno) if is_block: body = parser.parse_statements(['name:endmacrotag'], drop_needle=True) return nodes.CallBlock(call, [], {}, body, lineno=lineno) return nodes.Output([call])
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_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 call_method(self, name, args=None, kwargs=None, dyn_args=None, dyn_kwargs=None, lineno=None): if args is None: args = [] if kwargs is None: kwargs = [] return nodes.Call(self.attr(name, lineno=lineno), args, kwargs, dyn_args, dyn_kwargs, 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 test_is_method_call(self): node = self.env.parse('{{ foo() }}').find(nodes.Call) assert is_method_call(node, 'foo') is True assert is_method_call(node, 'bar') is False node = self.env.parse('{{ foo }}').find(nodes.Name) assert is_method_call(node, 'foo') is False node = nodes.Call() node.node = None assert is_method_call(node, 'foo') is False node = self.env.parse('{{ foo.bar.baz() }}').find(nodes.Call) assert is_method_call(node, 'baz') is True assert is_method_call(node, 'bar') is False assert is_method_call(node, 'foo') is False assert is_method_call(node, ('foo', 'bar', 'baz')) is True node = self.env.parse('{{ foo["bar"]() }}').find(nodes.Call) assert is_method_call(node, 'bar') is True
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 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, )
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', }
class Builtin(object): def __init__(self, name): self.name = name def __repr__(self): return self.name # Fields used by nodes.If(). IF_NODE_FIELDS = { 'test': nodes.Not( nodes.Call(nodes.Const(Builtin('isinstance')), [ nodes.Name(DJEDI_NODE_STORAGE, 'load'), nodes.Const(Builtin('dict')), ], [], None, None)), 'body': [ nodes.Assign(nodes.Name(DJEDI_NODE_STORAGE, 'store'), nodes.Dict([])), ], } # Construct the Jinja2 AST equivalent of: # # if not isinstance(DJEDI_NODE_STORAGE, dict): # DJEDI_NODE_STORAGE = {} # if nodes.If.fields == ('test', 'body', 'elif_', 'else_'): # Jinja 2.10 added the "elif" field to If() DJEDI_NODE_STORAGE_NODE = ( nodes.If(
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", }
def parse(self, parser): # Get the component for the tag name that we matched on tag_name = parser.stream.current[2] component_class = self.environment.components[tag_name] field_names = [f.name for f in dataclasses.fields(component_class)] has_children = CHILDREN_FIELD_NAME in field_names lineno = next(parser.stream).lineno node = nodes.Scope(lineno=lineno) # list of `Pair` nodes for tag properties to update "component" dictionary component_dict_update_items = [] while parser.stream.current.type != 'block_end': lineno = parser.stream.current.lineno if component_dict_update_items: parser.stream.expect('comma') name = parser.stream.expect('name') parser.stream.expect('assign') value = parser.parse_expression() component_dict_update_items.append( nodes.Pair(nodes.Const(name.value), value)) # dictionary initialization in the "component" name prepare_component_dict = [ self._initialize_component_dict(component_class, lineno) ] if component_dict_update_items: component_dict_delta = nodes.Dict(component_dict_update_items) # `Getattr` for "update" function of the dictionary "component" update_component_dict_fun = nodes.Getattr( nodes.Name(TMP_COMPONENT_DICT_NAME, 'load'), 'update', 'load') # `Call` for `component.update(<prop name>, <prop value>)` call_component_dict_update = nodes.Call(update_component_dict_fun, [component_dict_delta], [], None, None) prepare_component_dict.append( nodes.ExprStmt(call_component_dict_update)) # assign `component = __component` and `__component = None` prepare_component_dict.extend([ nodes.Assign(nodes.Name(COMPONENT_DICT_NAME, 'store', lineno=lineno), nodes.Name(TMP_COMPONENT_DICT_NAME, 'load', lineno=lineno), lineno=lineno), nodes.Assign(nodes.Name(TMP_COMPONENT_DICT_NAME, 'store', lineno=lineno), nodes.Const(None, lineno=lineno), lineno=lineno) ]) if has_children: inner_block = list( parser.parse_statements(('name:end' + tag_name, ), drop_needle=True)) # create children() macro children_macro = nodes.Macro() children_macro.name = CHILDREN_MACRO_NAME children_macro.args = [] children_macro.defaults = [] children_macro.body = inner_block children_macro_nodes = [children_macro] else: children_macro_nodes = [] # include tag template include_tag = nodes.Include() # use `template` item of the "component" dictionary for template path include_tag.template = nodes.Getitem(nodes.Name(COMPONENT_DICT_NAME, 'load', lineno=lineno), nodes.Const(TEMPLATE_FIELD_NAME, lineno=lineno), 'load', lineno=lineno) include_tag.ignore_missing = False include_tag.with_context = True node.body = prepare_component_dict + children_macro_nodes + [ include_tag, ] return node