some more stuff for jinja2
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 12 Apr 2008 23:10:18 +0000 (01:10 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 12 Apr 2008 23:10:18 +0000 (01:10 +0200)
--HG--
branch : trunk

bench.py
jinja2/compiler.py
jinja2/environment.py
jinja2/nodes.py
jinja2/optimizer.py
test_filter_and_linestatements.py

index 1ead1ecdb0a28968f7b0e6b7a675fe5bde5162f1..0e6b3072243a250bbd6b1f70467f765f15f424f3 100644 (file)
--- a/bench.py
+++ b/bench.py
-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)
index e291f1d03a880393c34bb3f5703324a064b581bf..e692184868b9abd69dd98d2ac4724e3a9d148dcf 100644 (file)
@@ -564,9 +564,9 @@ class CodeGenerator(NodeVisitor):
         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):
@@ -581,7 +581,7 @@ class CodeGenerator(NodeVisitor):
         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:
index fb8101be453fd08f308e2de2a14eb3fc40f6d350..8458a2381dbebe13cd096386a46cf4c8e7d90e06 100644 (file)
@@ -107,7 +107,6 @@ class Environment(object):
             source = self.parse(source, filename)
         node = optimize(source, self)
         source = generate(node, self, filename)
-        print source
         if raw:
             return source
         if isinstance(filename, unicode):
index 3b0ac3c87b6d83648bb49d46c7edd6b789be87c1..e1857d2290ab2b03f30f1e3f2c0f8641ba8ba723 100644 (file)
@@ -389,13 +389,14 @@ class Filter(Expr):
     """{{ 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:
index bd97fa05b4051c46d91a5dcbc32152260bd89489..d2550f49ef72493ef6505ab94f0fb6ac1a20449f 100644 (file)
@@ -95,6 +95,32 @@ class Optimizer(NodeTransformer):
         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:
index e5d94d0c6159a7974ad26456c2eb6ceef7d027fe..c9e8f956972609454c8ec7789e01f7d0760441ca 100644 (file)
@@ -16,6 +16,9 @@ tmpl = env.from_string("""\
 % endcall
 % filter escape
     <hello world>
+    % for item in [1, 2, 3]
+      -  ${item}
+    % endfor
 % endfilter
 """)