From 20eacb7fe12f0d0917a774b6fdafdf03ae1d4cbf Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 1 Mar 2007 21:00:48 +0100 Subject: [PATCH] [svn] implemented filter directive --HG-- branch : trunk --- jinja/nodes.py | 39 +++++++++++++ jinja/parser.py | 25 ++++++++- jinja/translators/python.py | 109 +++++++++++++++++++++++------------- 3 files changed, 133 insertions(+), 40 deletions(-) diff --git a/jinja/nodes.py b/jinja/nodes.py index ebca43a..a31b253 100644 --- a/jinja/nodes.py +++ b/jinja/nodes.py @@ -203,6 +203,26 @@ class Macro(Node): ) +class Filter(Node): + """ + Node for filter sections. + """ + + def __init__(self, lineno, body, filters): + self.lineno = lineno + self.body = body + self.filters = filters + + def get_items(self): + return [self.body, self.filters] + + def __repr__(self): + return 'Filter(%r)' % ( + self.body, + self.filters + ) + + class Block(Node): """ A node that represents a block. @@ -239,5 +259,24 @@ class Extends(Node): self.lineno = lineno self.template = template + def get_items(self): + return [self.template] + def __repr__(self): return 'Extends(%r)' % self.template + + +class Include(Node): + """ + A node that represents the include tag. + """ + + def __init__(self, lineno, template): + self.lineno = lineno + self.template = template + + def get_items(self): + return [self.template] + + def __repr__(self): + return 'Include(%r)' % self.template diff --git a/jinja/parser.py b/jinja/parser.py index c4a4728..bc5d2a8 100644 --- a/jinja/parser.py +++ b/jinja/parser.py @@ -23,6 +23,7 @@ switch_for = lambda p, t, d: t == 'name' and d in ('else', 'endfor') end_of_for = lambda p, t, d: t == 'name' and d == 'endfor' switch_if = lambda p, t, d: t == 'name' and d in ('else', 'elif', 'endif') end_of_if = lambda p, t, d: t == 'name' and d == 'endif' +end_of_filter = lambda p, t, d: t == 'name' and d == 'endfilter' end_of_macro = lambda p, t, d: t == 'name' and d == 'endmacro' end_of_block_tag = lambda p, t, d: t == 'name' and d == 'endblock' @@ -49,10 +50,12 @@ class Parser(object): 'for': self.handle_for_directive, 'if': self.handle_if_directive, 'cycle': self.handle_cycle_directive, + 'filter': self.handle_filter_directive, 'print': self.handle_print_directive, 'macro': self.handle_macro_directive, 'block': self.handle_block_directive, - 'extends': self.handle_extends_directive + 'extends': self.handle_extends_directive, + 'include': self.handle_include_directive } def handle_for_directive(self, lineno, gen): @@ -122,6 +125,15 @@ class Parser(object): # skip that. return nodes.Cycle(lineno, ast.expr.args[0]) + def handle_filter_directive(self, lineno, gen): + """ + Handle {% filter foo|bar %} directives. + """ + ast = self.parse_python(lineno, gen, '_filter(dummy|%s)') + body = self.subparse(end_of_filter, True) + self.close_remaining_block() + return nodes.Filter(lineno, body, ast.expr.args[0].nodes[1:]) + def handle_print_directive(self, lineno, gen): """ Handle {{ foo }} and {% print foo %}. @@ -186,7 +198,7 @@ class Parser(object): def handle_extends_directive(self, lineno, gen): """ - Handle extends directive used for inheritance. + Handle the extends directive used for inheritance. """ tokens = list(gen) if len(tokens) != 1 or tokens[0][1] != 'string': @@ -195,6 +207,15 @@ class Parser(object): raise TemplateSyntaxError('extends called twice', lineno) self.extends = nodes.Extends(lineno, tokens[0][2][1:-1]) + def handle_include_directive(self, lineno, gen): + """ + Handle the include directive used for template inclusion. + """ + tokens = list(gen) + if len(tokens) != 1 or tokens[0][1] != 'string': + raise TemplateSyntaxError('include requires a string', lineno) + return nodes.Include(lineno, tokens[0][2][1:-1]) + def parse_python(self, lineno, gen, template='%s'): """ Convert the passed generator into a flat string representing diff --git a/jinja/translators/python.py b/jinja/translators/python.py index 1f8e724..ee88789 100644 --- a/jinja/translators/python.py +++ b/jinja/translators/python.py @@ -86,7 +86,9 @@ class PythonTranslator(Translator): nodes.Cycle: self.handle_cycle, nodes.Print: self.handle_print, nodes.Macro: self.handle_macro, + nodes.Filter: self.handle_filter, nodes.Block: self.handle_block, + nodes.Include: self.handle_include, # used python nodes ast.Name: self.handle_name, ast.AssName: self.handle_name, @@ -142,6 +144,41 @@ class PythonTranslator(Translator): """ return (' ' * (self.indention * 4)) + text + def filter(self, s, filter_nodes): + """ + Apply a filter on an object. + """ + filters = [] + for n in filter_nodes: + if n.__class__ is ast.CallFunc: + if n.node.__class__ is not ast.Name: + raise TemplateSyntaxError('invalid filter. filter must ' + 'be a hardcoded function name ' + 'from the filter namespace', + n.lineno) + args = [] + for arg in n.args: + if arg.__class__ is ast.Keyword: + raise TemplateSyntaxError('keyword arguments for ' + 'filters are not supported.', + n.lineno) + args.append(self.handle_node(arg)) + if n.star_args is not None or n.dstar_args is not None: + raise TemplateSynaxError('*args / **kwargs is not supported ' + 'for filters', n.lineno) + filters.append('(%r, %s)' % ( + n.node.name, + _to_tuple(args) + )) + elif n.__class__ is ast.Name: + filters.append('(%r, ())' % n.name) + else: + raise TemplateSyntaxError('invalid filter. filter must be a ' + 'hardcoded function name from the ' + 'filter namespace', + n.lineno) + return 'apply_filters(%s, context, %s)' % (s, _to_tuple(filters)) + def handle_node(self, node): """ Handle one node @@ -186,8 +223,7 @@ class PythonTranslator(Translator): ' apply_filters = environment.apply_filters\n' ' call_function = environment.call_function\n' ' call_function_simple = environment.call_function_simple\n' - ' finish_var = environment.finish_var\n' - ' write_var = lambda x: write(finish_var(x))\n\n' + ' finish_var = environment.finish_var\n\n' ' # TEMPLATE CODE' ] self.indention += 1 @@ -304,9 +340,9 @@ class PythonTranslator(Translator): self.indention -= 1 if hardcoded: - write('write_var(context.current[%r].cycle())' % name) + write('write(finish_var(context.current[%r].cycle()))' % name) else: - write('write_var(context.current[%r].cycle(%s))' % ( + write('write(finish_var(context.current[%r].cycle(%s)))' % ( name, self.handle_node(node.seq) )) @@ -317,7 +353,7 @@ class PythonTranslator(Translator): """ Handle a print statement. """ - return self.indent('write_var(%s)' % self.handle_node(node.variable)) + return self.indent('write(finish_var(%s))' % self.handle_node(node.variable)) def handle_macro(self, node): """ @@ -339,6 +375,24 @@ class PythonTranslator(Translator): return '\n'.join(buf) + def handle_filter(self, node): + """ + Handle filter sections. + """ + buf = [] + write = lambda x: buf.append(self.indent(x)) + write('def filtered():') + self.indention += 1 + write('context.push()') + write('buffer = []') + write('write = buffer.append') + buf.append(self.handle_node(node.body)) + write('context.pop()') + write('return u\'\'.join(buffer)') + self.indention -= 1 + write('write(%s)' % self.filter('filtered()', node.filters)) + return '\n'.join(buf) + def handle_block(self, node): """ Handle blocks in the sourcecode. We only use them to @@ -365,6 +419,17 @@ class PythonTranslator(Translator): buf.append(self.indent('# END OF BLOCK')) return '\n'.join(buf) + def handle_include(self, node): + """ + Include another template at the current position. + """ + buf = [self.indent('# INCLUDED TEMPLATE %r' % node.filename)] + tmpl = self.environment.loader.parse(node.template, + node.filename) + buf.append(self.handle_node(tmpl)) + buf.append(self.indent('# END OF INCLUSION')) + return '\n'.join(buf) + # -- python nodes def handle_name(self, node): @@ -471,39 +536,7 @@ class PythonTranslator(Translator): """ We use the pipe operator for filtering. """ - filters = [] - for n in node.nodes[1:]: - if n.__class__ is ast.CallFunc: - if n.node.__class__ is not ast.Name: - raise TemplateSyntaxError('invalid filter. filter must ' - 'be a hardcoded function name ' - 'from the filter namespace', - n.lineno) - args = [] - for arg in n.args: - if arg.__class__ is ast.Keyword: - raise TemplateSyntaxError('keyword arguments for ' - 'filters are not supported.', - n.lineno) - args.append(self.handle_node(arg)) - if n.star_args is not None or n.dstar_args is not None: - raise TemplateSynaxError('*args / **kwargs is not supported ' - 'for filters', n.lineno) - filters.append('(%r, %s)' % ( - n.node.name, - _to_tuple(args) - )) - elif n.__class__ is ast.Name: - filters.append('(%r, ())' % n.name) - else: - raise TemplateSyntaxError('invalid filter. filter must be a ' - 'hardcoded function name from the ' - 'filter namespace', - n.lineno) - return 'apply_filters(%s, context, %s)' % ( - self.handle_node(node.nodes[0]), - _to_tuple(filters) - ) + return self.filter(self.handle_node(node.nodes[0]), node.nodes[1:]) def handle_call_func(self, node): """ -- 2.26.2