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'
'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):
# 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 %}.
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':
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
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,
"""
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
' 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
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)
))
"""
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):
"""
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
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):
"""
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):
"""