def parse_text(self, parser): import_node = parser.parse_import() target = import_node.target lineno = import_node.lineno return [ import_node, nodes.Assign( nodes.Name(target, 'store').set_lineno(lineno), nodes.Filter( nodes.Name(target, 'load').set_lineno(lineno), 'load_text', [], [], None, None ) .set_lineno(lineno) ).set_lineno(lineno) ]
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 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 compile_expression(self, source, undefined_to_none=True): """A handy helper method that returns a callable that accepts keyword arguments that appear as variables in the expression. If called it returns the result of the expression. This is useful if applications want to use the same rules as Jinja in template "configuration files" or similar situations. Example usage: >>> env = Environment() >>> expr = env.compile_expression('foo == 42') >>> expr(foo=23) False >>> expr(foo=42) True Per default the return value is converted to `None` if the expression returns an undefined value. This can be changed by setting `undefined_to_none` to `False`. >>> env.compile_expression('var')() is None True >>> env.compile_expression('var', undefined_to_none=False)() Undefined **new in Jinja 2.1** """ parser = Parser(self, source, state='variable') try: expr = parser.parse_expression() if not parser.stream.eos: raise TemplateSyntaxError('chunk after expression', parser.stream.current.lineno, None, None) except TemplateSyntaxError: self.handle_exception(sys.exc_info(), source_hint=source) body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] template = self.from_string(nodes.Template(body, lineno=1)) return TemplateExpression(template, undefined_to_none)
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_import(self, parser, converter): import_node = parser.parse_import() target = import_node.target lineno = import_node.lineno body = [ import_node, nodes.Assign( nodes.Name(target, "store").set_lineno(lineno), nodes.Filter( nodes.Name(target, "load").set_lineno(lineno), "load_{}".format(converter), [], [], None, None, ).set_lineno(lineno), ).set_lineno(lineno), ] return self._parse_profile_block(parser, import_node.template, "import_{}".format(converter), body, lineno)
def parse_component(self, parser, token): # first: just make a call to the corresponding macro of the component. # now define a second - nearly identical macro - for redrawing # "node" is the node which will be returned in place node = nodes.CallBlock(lineno=token.lineno) node.call = parser.parse_expression() if not isinstance(node.call, nodes.Call): parser.fail( 'the component call must be a "call": {% compo compo_name() %}', node.lineno) component_name = node.call.node.name if parser.stream.current.type == 'lparen': parser.parse_signature(node) else: node.args = [] node.defaults = [] original_body = parser.parse_statements(('name:endcompo', ), drop_needle=True) # inject a {% set self = COMPO_OBJ %} assign_node = nodes.Assign() assign_node.target = nodes.Name("self", "store") assign_node.node = nodes.Name(component_name, "load") node.body = [assign_node ] + original_body # node gets the additional "self"-hack ## # overwrites the local-name of the compo {% set COMPO_OBJ = self %} ## rev_assign_node = nodes.Assign() ## rev_assign_node.target = nodes.Name(component_name, "store") ## rev_assign_node.node = nodes.Name("self", "load") return node
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_experiment_enrolled_alternative(self, parser): """ Parse {% experiment_enrolled_alternative <experiment_name> %} tags """ lineno = parser.stream.current.lineno # list of nodes that will be used when calling the callback: args = [] # get experiment name from token experiment_name = parser.stream.current args.append(self._name_or_const(experiment_name)) next(parser.stream) # we will also need the context in the callback: args.append(nodes.ContextReference()) # expecting `as` after the alternatives: if not self._token_as(parser): raise TemplateSyntaxError( 'Syntax should be like: ' '{% experiment_enrolled_alternative "experiment_name"' ' as some_variable %}', lineno, ) next(parser.stream) # parse what comes after `as`: target = parser.parse_assign_target() # create a callback node that will be executed on render: call_node = self.call_method('render_experiment_enrolled_alternative', args, lineno=lineno) # return an assignment node that will trigger the callback: return nodes.Assign(target, call_node, lineno=lineno)
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 _initialize_component_dict(component_class, lineno): items = [] def pair_node_for_field(name, value): return nodes.Pair(nodes.Const(name, lineno=lineno), nodes.Const(value, lineno=lineno), lineno=lineno) for f in dataclasses.fields(component_class): if f.default is not MISSING: items.append(pair_node_for_field(f.name, f.default)) elif f.default_factory is not MISSING: # TODO here we could use `Call` node as the assigning expression items.append(pair_node_for_field(f.name, f.default_factory())) component_dict = nodes.Dict(items, lineno=lineno) # `Assign` dictionary to the "component" name return nodes.Assign(nodes.Name(TMP_COMPONENT_DICT_NAME, 'store', lineno=lineno), component_dict, 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 output(self, parser, block_call, target, tag_name, lineno): if target: target_node = nodes.Name(target, 'store', lineno=lineno) return nodes.Assign(target_node, block_call, lineno=lineno) call = nodes.MarkSafe(block_call, lineno=lineno) return nodes.Output([call], lineno=lineno)
>>> env.compile_expression('var', undefined_to_none=False)() Undefined **new in Jinja 2.1** """ parser = Parser(self, source, state='variable') try: expr = parser.parse_expression() if not parser.stream.eos: raise TemplateSyntaxError('chunk after expression', parser.stream.current.lineno, None, None) except TemplateSyntaxError, e: e.source = source raise e body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)] template = self.from_string(nodes.Template(body, lineno=1)) return TemplateExpression(template, undefined_to_none) def join_path(self, template, parent): """Join a template with the parent. By default all the lookups are relative to the loader root so this method returns the `template` parameter unchanged, but if the paths should be relative to the parent template, this function can be used to calculate the real template name. Subclasses may override this method and implement template path joining here. """ return template
def parse(self, parser): """Parse a translatable tag.""" lineno = next(parser.stream).lineno num_called_num = False # find all the variables referenced. Additionally a variable can be # defined in the body of the trans block too, but this is checked at # a later state. plural_expr = None plural_expr_assignment = None variables = {} while parser.stream.current.type != 'block_end': if variables: parser.stream.expect('comma') # skip colon for python compatibility if parser.stream.skip_if('colon'): break name = parser.stream.expect('name') if name.value in variables: parser.fail('translatable variable %r defined twice.' % name.value, name.lineno, exc=TemplateAssertionError) # expressions 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') if plural_expr is None: if isinstance(var, nodes.Call): plural_expr = nodes.Name('_trans', 'load') variables[name.value] = plural_expr plural_expr_assignment = nodes.Assign( nodes.Name('_trans', 'store'), var) else: plural_expr = var num_called_num = name.value == 'num' parser.stream.expect('block_end') plural = plural_names = None have_plural = False referenced = set() # now parse until endtrans or pluralize singular_names, singular = self._parse_block(parser, True) if singular_names: referenced.update(singular_names) if plural_expr is None: plural_expr = nodes.Name(singular_names[0], 'load') num_called_num = singular_names[0] == 'num' # if we have a pluralize block, we parse that too if parser.stream.current.test('name:pluralize'): have_plural = True next(parser.stream) if parser.stream.current.type != 'block_end': name = parser.stream.expect('name') if name.value not in variables: parser.fail('unknown variable %r for pluralization' % name.value, name.lineno, exc=TemplateAssertionError) plural_expr = variables[name.value] num_called_num = name.value == 'num' parser.stream.expect('block_end') plural_names, plural = self._parse_block(parser, False) next(parser.stream) referenced.update(plural_names) else: next(parser.stream) # register free names as simple name expressions for var in referenced: if var not in variables: variables[var] = nodes.Name(var, 'load') if not have_plural: plural_expr = None elif plural_expr is None: parser.fail('pluralize without variables', lineno) node = self._make_node(singular, plural, variables, plural_expr, bool(referenced), num_called_num and have_plural) node.set_lineno(lineno) if plural_expr_assignment is not None: return [plural_expr_assignment, node] else: return node
def parse(self, parser): """Parse a translatable tag.""" lineno = next(parser.stream).lineno num_called_num = False # find all the variables referenced. Additionally a variable can be # defined in the body of the trans block too, but this is checked at # a later state. plural_expr = None plural_expr_assignment = None variables = {} trimmed = None while parser.stream.current.type != "block_end": if variables: parser.stream.expect("comma") # skip colon for python compatibility if parser.stream.skip_if("colon"): break name = parser.stream.expect("name") if name.value in variables: parser.fail( "translatable variable %r defined twice." % name.value, name.lineno, exc=TemplateAssertionError, ) # expressions if parser.stream.current.type == "assign": next(parser.stream) variables[name.value] = var = parser.parse_expression() elif trimmed is None and name.value in ("trimmed", "notrimmed"): trimmed = name.value == "trimmed" continue else: variables[name.value] = var = nodes.Name(name.value, "load") if plural_expr is None: if isinstance(var, nodes.Call): plural_expr = nodes.Name("_trans", "load") variables[name.value] = plural_expr plural_expr_assignment = nodes.Assign( nodes.Name("_trans", "store"), var ) else: plural_expr = var num_called_num = name.value == "num" parser.stream.expect("block_end") plural = None have_plural = False referenced = set() # now parse until endtrans or pluralize singular_names, singular = self._parse_block(parser, True) if singular_names: referenced.update(singular_names) if plural_expr is None: plural_expr = nodes.Name(singular_names[0], "load") num_called_num = singular_names[0] == "num" # if we have a pluralize block, we parse that too if parser.stream.current.test("name:pluralize"): have_plural = True next(parser.stream) if parser.stream.current.type != "block_end": name = parser.stream.expect("name") if name.value not in variables: parser.fail( "unknown variable %r for pluralization" % name.value, name.lineno, exc=TemplateAssertionError, ) plural_expr = variables[name.value] num_called_num = name.value == "num" parser.stream.expect("block_end") plural_names, plural = self._parse_block(parser, False) next(parser.stream) referenced.update(plural_names) else: next(parser.stream) # register free names as simple name expressions for var in referenced: if var not in variables: variables[var] = nodes.Name(var, "load") if not have_plural: plural_expr = None elif plural_expr is None: parser.fail("pluralize without variables", lineno) if trimmed is None: trimmed = self.environment.policies["ext.i18n.trimmed"] if trimmed: singular = self._trim_whitespace(singular) if plural: plural = self._trim_whitespace(plural) node = self._make_node( singular, plural, variables, plural_expr, bool(referenced), num_called_num and have_plural, ) node.set_lineno(lineno) if plural_expr_assignment is not None: return [plural_expr_assignment, node] else: return node
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)
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( IF_NODE_FIELDS['test'], # test IF_NODE_FIELDS['body'], # body [], # elif
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 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 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_set(self): lineno = next(self.stream).lineno target = self.parse_assign_target() self.stream.expect('assign') expr = self.parse_tuple() return nodes.Assign(target, expr, lineno=lineno)
def _set_var(var_name, var_value, lineno): target_var = nodes.Name(var_name, 'store', lineno=lineno) return nodes.Assign(target_var, var_value, lineno=lineno)
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