-from jinja import Environment as E1
-from jinja2 import Environment as E2
-from mako.template import Template as M
-
-t1, t2 = [e.from_string("""
-<ul>
-{%- for item in seq %}
- <li>{{ item|e }}</li>
-{%- endfor %}
-</ul>
-""") for e in E1(), E2()]
-
-m = M("""
-<ul>
-% for item in seq:
- <li>${item|h}</li>
-% endfor
-</ul>
+from django.conf import settings
+settings.configure()
+from django.template import Template as DjangoTemplate, Context as DjangoContext
+from jinja2 import Environment as JinjaEnvironment
+from mako.template import Template as MakoTemplate
+from timeit import Timer
+
+
+jinja_template = JinjaEnvironment(
+ line_statement_prefix='%',
+ variable_start_string="${",
+ variable_end_string="}"
+).from_string("""\
+<!doctype html>
+<html>
+ <head>
+ <title>${page_title|e}
+ </head>
+ <body>
+ <div class="header">
+ <h1>${page_title|e}</h1>
+ </div>
+ <ul class="navigation">
+ % for href, caption in [
+ ('index.html', 'Index'),
+ ('downloads.html', 'Downloads'),
+ ('products.html', 'Products')
+ ]
+ <li><a href="${href|e}">${caption|e}</a></li>
+ % endfor
+ </ul>
+ <div class="table">
+ <table>
+ % for row in table
+ <tr>
+ % for cell in row
+ <td>${cell}</td>
+ % endfor
+ </tr>
+ % endfor
+ </table>
+ </div>
+ </body>
+</html>\
+""")
+
+django_template = DjangoTemplate("""\
+<!doctype html>
+<html>
+ <head>
+ <title>{{ page_title }}
+ </head>
+ <body>
+ <div class="header">
+ <h1>{{ page_title }}</h1>
+ </div>
+ <ul class="navigation">
+ {% for href, caption in navigation %}
+ <li><a href="{{ href }}">{{ caption }}</a></li>
+ {% endfor %}
+ </ul>
+ <div class="table">
+ <table>
+ {% for row in table %}
+ <tr>
+ {% for cell in row %}
+ <td>{{ cell }}</td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ </body>
+</html>\
+""")
+
+mako_template = MakoTemplate("""\
+<!doctype html>
+<html>
+ <head>
+ <title>${page_title|h}
+ </head>
+ <body>
+ <div class="header">
+ <h1>${page_title|h}</h1>
+ </div>
+ <ul class="navigation">
+ % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
+ <li><a href="${href|h}">${caption|h}</a></li>
+ % endfor
+ </ul>
+ <div class="table">
+ <table>
+ % for row in table:
+ <tr>
+ % for cell in row:
+ <td>${cell}</td>
+ % endfor
+ </tr>
+ % endfor
+ </table>
+ </div>
+ </body>
+</html>\
""")
+
+context = {
+ 'page_title': 'mitsuhiko\'s benchmark',
+ 'table': [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) for x in range(1000)]
+}
+
+def test_jinja():
+ jinja_template.render(context)
+
+def test_django():
+ c = DjangoContext(context)
+ c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
+ django_template.render(c)
+
+def test_mako():
+ mako_template.render(**context)
+
+
+for test in 'jinja', 'mako', 'django':
+ t = Timer(setup='from __main__ import test_%s as bench' % test,
+ stmt='bench()')
+ print '%-20s%.4fms' % (test, t.timeit(number=20) / 20)
for arg in node.defaults:
self.visit(arg)
self.write(', ')
- self.write('), %r, %r)' % (
- macro_frame.accesses_arguments,
- macro_frame.accesses_caller
+ self.write('), %s, %s)' % (
+ macro_frame.accesses_arguments and '1' or '0',
+ macro_frame.accesses_caller and '1' or '0'
))
def visit_CallBlock(self, node, frame):
for arg in node.defaults:
self.visit(arg)
self.write(', ')
- self.write('), %r, False)' % call_frame.accesses_arguments)
+ self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
if frame.buffer is None:
self.writeline('yield ', node)
else:
"""{{ foo|bar|baz }}"""
fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
- def as_const(self):
- if self.node is None:
+ def as_const(self, obj=None):
+ if self.node is obj is None:
raise Impossible()
filter = self.environment.filters.get(self.name)
if filter is None or getattr(filter, 'contextfilter', False):
raise nodes.Impossible()
- obj = self.node.as_const()
+ if obj is None:
+ obj = self.node.as_const()
args = [x.as_const() for x in self.args]
kwargs = dict(x.as_const() for x in self.kwargs)
if self.dyn_args is not None:
finally:
context.pop()
+ def visit_FilterBlock(self, node, context):
+ """Try to filter a block at compile time."""
+ node = self.generic_visit(node, context)
+ context.push()
+
+ # check if we can evaluate the wrapper body into a string
+ # at compile time
+ buffer = []
+ for child in node.body:
+ if not isinstance(child, nodes.Output):
+ return node
+ for item in child.optimized_nodes():
+ if isinstance(item, nodes.Node):
+ return node
+ buffer.append(item)
+
+ # now check if we can evaluate the filter at compile time.
+ try:
+ data = node.filter.as_const(u''.join(buffer))
+ except nodes.Impossible:
+ return node
+
+ context.pop()
+ const = nodes.Const(data, lineno=node.lineno)
+ return nodes.Output([const], lineno=node.lineno)
+
def visit_For(self, node, context):
"""Loop unrolling for iterable constant values."""
try: