def parse(self, parser): stream = parser.stream lineno = six.next(stream).lineno args = [] kwargs = [] while stream.current.type != 'block_end': if stream.current.type == 'name' and \ stream.look().type == 'assign': key = nodes.Const(six.next(stream).value) stream.skip() value = parser.parse_expression() kwargs.append(nodes.Pair(key, value, lineno=value.lineno)) else: if args: parser.fail('jsdir tag takes only one non-keyword ' 'argument') if kwargs: parser.fail('Args cannot be provided after kwargs', parser.stream.current.lineno) args.append(parser.parse_expression()) return nodes.Output([ self.call_method('get_tags', args=[nodes.List(args), nodes.Dict(kwargs)]) ]).set_lineno(lineno)
def parse(self, parser): tag = parser.stream.next() attrs = self.parse_attrs(parser) attrs = nodes.Dict([nodes.Pair(nodes.Const(k), v) for k,v in attrs.items()]) return nodes.Output([self.call_method('_call_simple_tag', args=[attrs])])
def make_call_node(*kw): return self.call_method('_reverse', args=[ viewname, nodes.List(args), nodes.Dict(kwargs), nodes.Name('_current_app', 'load'), ], kwargs=kw)
def _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_call_node(*kw): return self.call_method('_call', args=[ nodes.List(args), nodes.Dict(kwargs), ], kwargs=kw)
def parse(self, parser): lineno = next(parser.stream).lineno options = dict() # Parse the arguments source = parser.parse_expression() if parser.stream.skip_if('comma'): first = True while parser.stream.current.type != 'block_end': if not first: parser.stream.expect('comma') first = False # Lookahead to see if this is an assignment (an option) if parser.stream.current.test('name') and parser.stream.look().test('assign'): name = next(parser.stream).value parser.stream.skip() value = parser.parse_expression() options[nodes.Const(name)] = value node_options = [] for k, v in options.items(): node_options.append(nodes.Pair(k, v)) node_options = nodes.Dict(node_options) call = self.call_method('render', [source, node_options], lineno=lineno) output = nodes.CallBlock(call, [], [], []) output.set_lineno(lineno) return output
def _get_params(self, parser): """ Parses the statement to collect the parameters given Returns: - `nodes.Dict` - A dictionary node containing instances of `nodes.Pair` representing the key/value pairs in the context """ # Argument parsing adapted from https://github.com/coffin/coffin/blob/master/coffin/common.py#L164 stream = parser.stream kwargs = [] eval_ctx = nodes.EvalContext(self.environment) while not stream.current.test_any('block_end'): if kwargs: stream.expect('comma') if stream.current.test('name') and stream.look().test('assign'): key = nodes.Const(next(stream).value) stream.skip() value = parser.parse_expression() kwargs.append(nodes.Pair(key, value, lineno=key.lineno)) if not kwargs: parser.fail( '`include_with` tag must have parameters. Use `include` instead', lineno=stream.current.lineno) kwargs = nodes.Dict(kwargs) return kwargs
def make_call_node(*kw): return self.call_method('_reverse', args=[ viewname, nodes.List(args), nodes.Dict(kwargs), nodes.ContextReference(), ], kwargs=kw)
def parse(self, parser): tag = parser.stream.next() attrs = self.parse_attrs(parser) attrs = nodes.Dict([nodes.Pair(nodes.Const(k), v) for k,v in attrs.items()]) body = parser.parse_statements(['name:end'+tag.value], drop_needle=True) return [nodes.CallBlock(self.call_method('_call_simple_block', args=[attrs]), [], [], body).set_lineno(tag.lineno)]
def parse(self, parser): stream = parser.stream tag = stream.next() # get view name if stream.current.test('string'): viewname = parser.parse_primary() else: # parse valid tokens and manually build a string from them bits = [] name_allowed = True while True: if stream.current.test_any('dot', 'sub'): bits.append(stream.next()) name_allowed = True elif stream.current.test('name') and name_allowed: bits.append(stream.next()) name_allowed = False else: break viewname = nodes.Const("".join([b.value for b in bits])) if not bits: raise TemplateSyntaxError( "'%s' requires path to view" % tag.value, tag.lineno) # get arguments args = [] kwargs = [] while not stream.current.test_any('block_end', 'name:as'): if args or kwargs: stream.expect('comma') if stream.current.test('name') and stream.look().test('assign'): key = nodes.Const(stream.next().value) stream.skip() value = parser.parse_expression() kwargs.append(nodes.Pair(key, value, lineno=key.lineno)) else: args.append(parser.parse_expression()) make_call_node = lambda *kw: \ self.call_method('_reverse', args=[viewname, nodes.List(args), nodes.Dict(kwargs)], kwargs=kw) # if an as-clause is specified, write the result to context... if stream.next_if('name:as'): var = nodes.Name(stream.expect('name').value, 'store') call_node = make_call_node( nodes.Keyword('fail', nodes.Const(False))) return nodes.Assign(var, call_node) # ...otherwise print it out. else: return nodes.Output([make_call_node()]).set_lineno(tag.lineno)
def _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_attrs(self, parser, add_id=True, with_context=False): attrs = {} while parser.stream.current.type != 'block_end': node = parser.parse_assign_target(with_tuple=False) if parser.stream.skip_if('assign'): attrs[node.name] = parser.parse_expression() else: attrs[node.name] = nodes.Const(node.name) if with_context: attrs['ctx'] = nodes.ContextReference() return nodes.Dict( [nodes.Pair(nodes.Const(k), v) for k, v in attrs.items()])
def parse(self, parser): lineno = parser.stream.expect("name:ktl_component").lineno component_name = parser.parse_expression() component_props = [] while parser.stream.current.type != "block_end": target = nodes.Const(parser.parse_assign_target(name_only=True).name) value = parser.parse_expression() if parser.stream.skip_if("assign") else nodes.Const(True) component_props.append(nodes.Pair(target, value)) result = self.call_method("_render", [component_name, nodes.Dict(component_props)], lineno=lineno) return nodes.Output([result], lineno=lineno)
def parse_dict(self): token = self.stream.expect('lbrace') items = [] while self.stream.current.type != 'rbrace': if items: self.stream.expect('comma') if self.stream.current.type == 'rbrace': break key = self.parse_expression() self.stream.expect('colon') value = self.parse_expression() items.append(nodes.Pair(key, value, lineno=key.lineno)) self.stream.expect('rbrace') return nodes.Dict(items, lineno=token.lineno)
def parse(self, parser): # Tag information token = next(parser.stream) lineno = token.lineno tag = token.value if tag == DJEDI_INIT_TAG: return [ node.set_lineno(lineno) for node in self.create_node_storage() ] # Parse arguments uri = parser.parse_expression() params = self.parse_params(parser) body = [] # If this is a blocknode, parse the body too. if tag == DJEDI_BLOCK_TAG: body = parser.parse_statements( ['name:end{}'.format(DJEDI_BLOCK_TAG)], drop_needle=True) if body: default = body[0].nodes[0].data.rstrip('\n\r ') default = textwrap.dedent(default) default = nodes.Const(default) else: default = nodes.Const(None) else: default = params.pop('default', nodes.Const(None)) edit = params.pop('edit', nodes.Const(True)) # If we got passed const values, we can buffer nodes before render. can_buffer = all([isinstance(n, nodes.Const) for n in (uri, default)]) if can_buffer: node_or_uri = self.buffer_node(parser, uri, default) else: node_or_uri = uri params_dict = nodes.Dict([ nodes.Pair(nodes.Const(key), value) for key, value in params.items() ]) args = [node_or_uri, default, edit, params_dict, nodes.Const(tag)] return nodes.CallBlock(self.call_method('_render_node', args=args), [], [], body).set_lineno(lineno)
def parse(self, parser): stream = parser.stream tag = stream.next() # get view name if stream.current.test('string'): bundle_name = parser.parse_primary() else: bundle_name = parser.parse_expression() # get arguments args = [] kwargs = [] while not stream.current.test_any('block_end', 'name:as'): if args or kwargs: stream.expect('comma') if stream.current.test('name') and stream.look().test('assign'): key = nodes.Const(stream.next().value) stream.skip() value = parser.parse_expression() kwargs.append(nodes.Pair(key, value, lineno=key.lineno)) else: args.append(parser.parse_expression()) make_call_node = lambda *kw: \ self.call_method('_build_tag', args=[bundle_name, nodes.List(args), nodes.Dict(kwargs)], kwargs=kw) # if an as-clause is specified, write the result to context... if stream.next_if('name:as'): var = nodes.Name(stream.expect('name').value, 'store') call_node = make_call_node( nodes.Keyword('fail', nodes.Const(False))) return nodes.Assign(var, call_node) # ...otherwise print it out. else: return nodes.Output([make_call_node()]).set_lineno(tag.lineno)
def _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(self, parser): lineno = parser.stream.next().lineno # extract the language if available # Any additional parameters are passed to HtmlFormatter lang = None parameters = [] while parser.stream.current.type != 'block_end': if lang or parameters: parser.stream.expect('comma') name = parser.stream.expect('name') if name.value in parameters or (name.value == 'lang' and lang): parser.fail('parameter %r defined twice.' % name.value, name.lineno, exc=TemplateAssertionError) if parser.stream.current.type == 'assign': next(parser.stream) if name.value == 'lang': lang = parser.parse_expression() else: parameters.append( nodes.Pair(nodes.Const(name.value), parser.parse_expression())) if lang == None: lang = nodes.Const(None) parameters = nodes.Dict(parameters) # body of the block body = parser.parse_statements(['name:endhighlight'], drop_needle=True) return nodes.CallBlock( self.call_method('_highlight', [lang, parameters]), [], [], body).set_lineno(lineno)
def parse(self, parser): """Parse a translatable tag.""" lineno = next(parser.stream).lineno # 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 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: plural_expr = var 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') # 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] 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') # no variables referenced? no need to escape if not referenced: singular = singular.replace('%%', '%') if plural: plural = plural.replace('%%', '%') if not have_plural: plural_expr = None elif plural_expr is None: parser.fail('pluralize without variables', lineno) if variables: variables = nodes.Dict([ nodes.Pair(nodes.Const(x, lineno=lineno), y) for x, y in variables.items() ]) else: variables = None node = self._make_node(singular, plural, variables, plural_expr) node.set_lineno(lineno) return node
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 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
def to_node_dict(cls, d): return nodes.Dict([nodes.Pair(nodes.Const(k), v if isinstance(v, nodes.Const) else nodes.Const(v)) for k,v in d.items()])