def unless(token: "Token", parser: "Parser") -> nodes.Node: """The unless tag {% unless ... %} ... {% endunless %} Args: token: The token matches tag name parser: The parser Returns: The parsed node """ node = result = nodes.If(lineno=token.lineno) while True: node.test = nodes.Not( parser.parse_tuple(with_condexpr=False), lineno=token.lineno, ) node.body = parser.parse_statements( ("name:elif", "name:elsif", "name:else", "name:endunless") ) node.elif_ = [] node.else_ = [] token = next(parser.stream) if token.test_any("name:elif", "name:elsif"): node = nodes.If(lineno=parser.stream.current.lineno) result.elif_.append(node) continue if token.test("name:else"): result.else_ = parser.parse_statements( ("name:endunless",), drop_needle=True ) break return result
def parse(self, parser): tag = parser.stream.current lineno = next(parser.stream).lineno cond = parser.parse_expression() ### first, seek to the start of the "head" token parser.parse_statements(["name:head"], drop_needle=True) h_then = parser.parse_statements(["name:else", "name:body"]) if parser.stream.current.value == "else": next(parser.stream) h_else = parser.parse_statements(["name:body"]) else: h_else = None next(parser.stream) body = parser.parse_statements(["name:endonion", "name:tail"]) result = [nodes.If(cond, h_then, h_else)] result.extend(body) if parser.stream.current.value == "tail": next(parser.stream) t_then = parser.parse_statements(["name:else", "name:endonion"]) if parser.stream.current.value == "else": next(parser.stream) t_else = parser.parse_statements(["name:endonion"]) else: t_else = None result.append(nodes.If(cond, t_then, t_else)) next(parser.stream) return result
def parse(self, parser): lineno = next(parser.stream).lineno url = parser.parse_expression(with_condexpr=False) url_var = nodes.Name('checked_url', 'store') args = None if parser.stream.current.type != 'block_end': parser.stream.expect('comma') args = parser.parse_expression(with_condexpr=False) fun_var = parser.free_identifier() body = parser.parse_statements(('name:endauth', 'name:else')) token = next(parser.stream) if token.test('name:else'): else_ = parser.parse_statements( ('name:endauth', ), drop_needle=True) else: else_ = None url_fun_tuple = nodes.Tuple([url_var, fun_var], 'store') # The url goes in the dyn_args (its not visited otherwise). # To be in the dyn_args, it must be wrapped in a Tuple. assignment = nodes.Assign(url_fun_tuple, self.call_method( 'template_if_auth_url_for', dyn_args=nodes.Tuple([url], 'load'), dyn_kwargs=args)).set_lineno(lineno) returned_ast = [assignment] if_node = nodes.If() if_node.test = nodes.Name('checked_url', 'load') if_node.body = body if_node.else_ = else_ if_node.elif_ = [] returned_ast.append(if_node.set_lineno(lineno)) return returned_ast
def test_if_branching_multi_scope(): for_loop = nodes.For( nodes.Name("item", "store"), nodes.Name("seq", "load"), [ nodes.If( nodes.Name("expression", "load"), [nodes.Assign(nodes.Name("x", "store"), nodes.Const(42))], [], [], ), nodes.Include(nodes.Const("helper.html"), True, False), ], [], None, False, ) tmpl = nodes.Template( [nodes.Assign(nodes.Name("x", "store"), nodes.Const(23)), for_loop]) tmpl_sym = symbols_for_node(tmpl) for_sym = symbols_for_node(for_loop, tmpl_sym) assert for_sym.stores == set(["item", "x"]) assert for_sym.loads == { "l_1_x": ("alias", "l_0_x"), "l_1_item": ("param", None), "l_1_expression": ("resolve", "expression"), }
def parse_if(self): """Parse an if construct.""" node = result = nodes.If(lineno=self.stream.expect('name:if').lineno) while 1: node.test = self.parse_tuple(with_condexpr=False) node.body = self.parse_statements( ('name:elif', 'name:else', 'name:endif')) node.elif_ = [] node.else_ = [] token = next(self.stream) if token.test('name:elif'): node = nodes.If(lineno=self.stream.current.lineno) result.elif_.append(node) continue elif token.test('name:else'): result.else_ = self.parse_statements(('name:endif', ), drop_needle=True) break return result
def parse_if(self): node = result = nodes.If(lineno=self.stream.expect('name:if').lineno) while 1: node.test = self.parse_tuple(with_condexpr=False) node.body = self.parse_statements( ('name:elif', 'name:else', 'name:endif')) token = next(self.stream) if token.test('name:elif'): new_node = nodes.If(lineno=self.stream.current.lineno) node.else_ = [new_node] node = new_node continue elif token.test('name:else'): node.else_ = self.parse_statements(('name:endif', ), drop_needle=True) else: node.else_ = [] break return result
def _parse_qblock(self, parser, lineno): name = parser.parse_assign_target(name_only=True).name body = parser.parse_statements(['name:endqblock'], drop_needle=True) return nodes.If( nodes.Compare( # name in __blocks__ nodes.Const(name), [nodes.Operand('in', nodes.Name('__blocks__', 'load'))]), body, [], # elif_ [] # orelse ).set_lineno(lineno)
def parse(self, parser): m = nodes.Macro(lineno=next(parser.stream).lineno) name = parser.parse_assign_target(name_only=True).name m.name = f"default_{name}" parser.parse_signature(m) m.body = parser.parse_statements(("name:enddefaultmacro",), drop_needle=True) if_stmt = nodes.If( nodes.Not(nodes.Name(name, "load")), [nodes.Macro(name, m.args, m.defaults, m.body)], [], [], ) return [m, if_stmt]
def test_if_branching_stores(): tmpl = nodes.Template([ nodes.If(nodes.Name('expression', 'load'), [ nodes.Assign(nodes.Name('variable', 'store'), nodes.Const(42))], [])]) sym = symbols_for_node(tmpl) assert sym.refs == { 'variable': 'l_0_variable', 'expression': 'l_0_expression' } assert sym.stores == set(['variable']) assert sym.loads == { 'l_0_variable': ('resolve', 'variable'), 'l_0_expression': ('resolve', 'expression') } assert sym.dump_stores() == { 'variable': 'l_0_variable', }
def test_if_branching_multi_scope(): for_loop = nodes.For(nodes.Name('item', 'store'), nodes.Name('seq', 'load'), [ nodes.If(nodes.Name('expression', 'load'), [ nodes.Assign(nodes.Name('x', 'store'), nodes.Const(42))], []), nodes.Include(nodes.Const('helper.html'), True, False) ], [], None, False) tmpl = nodes.Template([ nodes.Assign(nodes.Name('x', 'store'), nodes.Const(23)), for_loop ]) tmpl_sym = symbols_for_node(tmpl) for_sym = symbols_for_node(for_loop, tmpl_sym) assert for_sym.stores == set(['item', 'x']) assert for_sym.loads == { 'l_1_x': ('alias', 'l_0_x'), 'l_1_item': ('param', None), 'l_1_expression': ('resolve', 'expression'), }
def parse(self, parser): # Create a normal With node first # Borrowing the codes from parser.py, # the only difference is the end tag is `endrequired` # instead of `endwith` with_node = nodes.With(lineno=next(parser.stream).lineno) targets = [] values = [] while parser.stream.current.type != 'block_end': if targets: parser.stream.expect('comma') target = parser.parse_assign_target() target.set_ctx('param') targets.append(target) parser.stream.expect('assign') values.append(parser.parse_expression()) with_node.targets = targets with_node.values = values with_node.body = parser.parse_statements(('name:endrequired', ), drop_needle=True) # Manually create a If node if_node = nodes.If() # If only one variable is required, assigned that variable to test if it is empty if len(values) == 1: test = values[0] else: # If more than one variables are required, concat them into a And node test = nodes.And(left=values[0], right=values[1]) for i in range(2, len(values)): test = nodes.And(left=test, right=values[i]) if_node.test = test # Assign with_node as the body of the if_node, to nest them if_node.body = [with_node] # set other fields to empty list. for _f in nodes.If: if getattr(if_node, _f) is None: setattr(if_node, _f, []) return if_node
def parse(self, parser): lineno = next(parser.stream).lineno # Get the block selection from the tag argument. block_selection = parser.parse_expression() # Make "Name" node for "For" node target. block_name = nodes.Name('_ext_ob_blknm', 'store') # For each block, add an "If" node checking if it matches the target. # Also record the original "default" ordering of the blocks. blocks = [] block_names = [] for node in parser.parse_statements(['name:endorderblocks'], drop_needle=True): if isinstance(node, nodes.Block): blocks.append(nodes.If( nodes.Compare( block_name, [nodes.Operand('eq', nodes.Const(node.name))]), [node], [])) block_names.append(node.name) # Make a "For" node iterating over the given block selection. If the # block selection is undefined or None then use the original ordering. return nodes.For( block_name, nodes.CondExpr( nodes.And( nodes.Test(block_selection, 'defined', [], [], None, None), nodes.Not( nodes.Test(block_selection, 'none', [], [], None, None)), ), block_selection, nodes.Tuple([nodes.Const(x) for x in block_names], 'store')), blocks, [], None, False)
def test_if_branching_stores(): tmpl = nodes.Template([ nodes.If( nodes.Name("expression", "load"), [nodes.Assign(nodes.Name("variable", "store"), nodes.Const(42))], [], [], ) ]) sym = symbols_for_node(tmpl) assert sym.refs == { "variable": "l_0_variable", "expression": "l_0_expression" } assert sym.stores == set(["variable"]) assert sym.loads == { "l_0_variable": ("resolve", "variable"), "l_0_expression": ("resolve", "expression"), } assert sym.dump_stores() == { "variable": "l_0_variable", }
class Builtin(object): def __init__(self, name): self.name = name def __repr__(self): return self.name DJEDI_NODE_STORAGE_NODE = ( nodes.If( # if not isinstance(DJEDI_NODE_STORAGE, dict): nodes.Not( nodes.Call(nodes.Const(Builtin('isinstance')), [ nodes.Name(DJEDI_NODE_STORAGE, 'load'), nodes.Const(Builtin('dict')), ], [], None, None)), # DJEDI_NODE_STORAGE = {} [ nodes.Assign(nodes.Name(DJEDI_NODE_STORAGE, 'store'), nodes.Dict( [])), ], # else: pass [])) class NodeExtension(Extension): tags = set([DJEDI_INIT_TAG, DJEDI_TAG, DJEDI_BLOCK_TAG]) def create_node_id(self, uri, default): m = hashlib.sha256() m.update(uri.encode('utf8')) if default:
'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( IF_NODE_FIELDS['test'], # test IF_NODE_FIELDS['body'], # body [], # elif [], # else )) else: # nodes.If.fields is assumed to be ('test', 'body', 'else') DJEDI_NODE_STORAGE_NODE = ( nodes.If( IF_NODE_FIELDS['test'], # test IF_NODE_FIELDS['body'], # body [] # else )) class NodeExtension(Extension): tags = set([DJEDI_INIT_TAG, DJEDI_TAG, DJEDI_BLOCK_TAG])
def case(token: "Token", parser: "Parser") -> nodes.Node: """The case-when tag {% case x %}{% when y %} ... {% endcase %} Args: token: The token matches tag name parser: The parser Returns: The parsed node """ lhs = parser.parse_tuple(with_condexpr=False) # %} if not parser.stream.skip_if("block_end"): raise TemplateSyntaxError( # pragma: no cover "Expected 'end of statement block'", token.lineno, ) token = next(parser.stream) if token.type == "data": if token.value.strip(): raise TemplateSyntaxError( "Expected nothing or whitespaces between case and when, " f"but got {token}", token.lineno, ) token = next(parser.stream) if token.type != "block_begin": raise TemplateSyntaxError( "Expected 'begin of statement block', " f"but got {token}", token.lineno, ) token = parser.stream.expect("name:when") node = result = nodes.If(lineno=token.lineno) while True: node.test = nodes.Compare( lhs, [ nodes.Operand( "eq", parser.parse_tuple(with_condexpr=False), ) ], lineno=token.lineno, ) node.body = parser.parse_statements( ("name:when", "name:else", "name:endcase") ) node.elif_ = [] node.else_ = [] token = next(parser.stream) if token.test("name:when"): node = nodes.If(lineno=parser.stream.current.lineno) result.elif_.append(node) continue if token.test("name:else"): result.else_ = parser.parse_statements( ("name:endcase",), drop_needle=True ) break return result
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 _if_node(self, cond, then_block, else_block): return nodes.If(cond, then_block, [], else_block)
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', }