[svn] implemented filter directive
authorArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 Mar 2007 20:00:48 +0000 (21:00 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 Mar 2007 20:00:48 +0000 (21:00 +0100)
--HG--
branch : trunk

jinja/nodes.py
jinja/parser.py
jinja/translators/python.py

index ebca43af4eccd0ca7714c4a5ecff6f57026c382f..a31b253926502b1c48dca16956b0df5299e10e64 100644 (file)
@@ -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
index c4a4728579d60455c531f4dec0587279854f235b..bc5d2a81a5053e28944a4dc18b43b97c1e187a0c 100644 (file)
@@ -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
index 1f8e7248bf335226167169c7a029bd9ded2ee656..ee88789099d6f16fa82cb9b4e581b62fbf5f2cb0 100644 (file)
@@ -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):
         """