added a semi realworld benchmark (jinja2 and mako)
authorArmin Ronacher <armin.ronacher@active-4.com>
Thu, 15 May 2008 20:47:27 +0000 (22:47 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Thu, 15 May 2008 20:47:27 +0000 (22:47 +0200)
--HG--
branch : trunk

examples/rwbench/jinja/helpers.html [new file with mode: 0644]
examples/rwbench/jinja/index.html [new file with mode: 0644]
examples/rwbench/jinja/layout.html [new file with mode: 0644]
examples/rwbench/mako/helpers.html [new file with mode: 0644]
examples/rwbench/mako/index.html [new file with mode: 0644]
examples/rwbench/mako/layout.html [new file with mode: 0644]
examples/rwbench/rwbench.py [new file with mode: 0644]
jinja2/compiler.py
jinja2/environment.py
jinja2/runtime.py

diff --git a/examples/rwbench/jinja/helpers.html b/examples/rwbench/jinja/helpers.html
new file mode 100644 (file)
index 0000000..89976aa
--- /dev/null
@@ -0,0 +1,12 @@
+{% macro input_field(name, value='', type='text') -%}
+  <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
+{%- endmacro %}
+
+{% macro textarea(name, value='', rows=10, cols=40) -%}
+  <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{
+    value|e }}</textarea>
+{%- endmacro %}
+
+{% macro form(action='', method='post') -%}
+  <form action="{{ action|e }}" method="{{ method }}">{{ caller() }}</form>
+{%- endmacro %}
diff --git a/examples/rwbench/jinja/index.html b/examples/rwbench/jinja/index.html
new file mode 100644 (file)
index 0000000..2b97e70
--- /dev/null
@@ -0,0 +1,26 @@
+{% extends "layout.html" %}
+{% from "helpers.html" import input_field, textarea, form %}
+{% block page_title %}Index Page{% endblock %}
+{% block body %}
+  {%- for article in articles %}
+  <div class="article">
+    <h2><a href="{{ article.href|e }}">{{ article.title|e }}</a></h2>
+    <p class="meta">written by <a href="{{ article.user.href|e
+      }}">{{ article.user.username|e }}</a> on {{ article.pub_date|dateformat }}</p>
+    <div class="text">{{ article.body }}</div>
+  </div>
+  {%- endfor %}
+  {%- call form() %}
+    <dl>
+      <dt>Name</dt>
+      <dd>{{ input_field('name') }}</dd>
+      <dt>E-Mail</dt>
+      <dd>{{ input_field('email') }}</dd>
+      <dt>URL</dt>
+      <dd>{{ input_field('url') }}</dd>
+      <dt>Comment</dd>
+      <dd>{{ textarea('comment') }}</dd>
+    </dl>
+    {{ input_field(type='submit', value='Submit') }}
+  {%- endcall %}
+{% endblock %}
diff --git a/examples/rwbench/jinja/layout.html b/examples/rwbench/jinja/layout.html
new file mode 100644 (file)
index 0000000..755789e
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+  <title>{% block page_title %}{% endblock %} | RealWorld Benchmark</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+</head>
+<body>
+  <div class="contents">
+    <div class="header">
+      <h1>RealWorld Benchmark</h1>
+      <blockquote><p>
+        A less stupid benchmark for Mako and Jinja2 to get an impression how
+        code changes affect runtime performance.
+      </p></blockquote>
+    </div>
+    <ul class="navigation">
+    {%- for href, caption in page_navigation %}
+      <li><a href="{{ href|e }}">{{ caption }}</a></li>
+    {%- endfor %}
+    </ul>
+    <div class="body">
+      {% block body %}{% endblock %}
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008 by I don't know who.
+    </div>
+  </div>
+</body>
+</html>
diff --git a/examples/rwbench/mako/helpers.html b/examples/rwbench/mako/helpers.html
new file mode 100644 (file)
index 0000000..a0290eb
--- /dev/null
@@ -0,0 +1,11 @@
+<%def name="input_field(name='', value='', type='text')">
+  <input type="${type}" value="${value|h}" name="${name}">
+</%def>
+
+<%def name="textarea(name, value='', rows=10, cols=40)">
+  <textarea name="${name}" rows="${rows}" cols="${cols}">${value|h}</textarea>
+</%def>
+
+<%def name="form(action='', method='post')">
+  <form action="${action|h}" method="${method}">${caller.body()}</form>
+</%def>
diff --git a/examples/rwbench/mako/index.html b/examples/rwbench/mako/index.html
new file mode 100644 (file)
index 0000000..33bfe32
--- /dev/null
@@ -0,0 +1,27 @@
+<%!
+  from rwbench import dateformat
+%>
+<%inherit file="layout.html" />
+<%namespace file="helpers.html" import="input_field, textarea, form" />
+<%def name="page_title()">Index Page</%def>
+% for article in articles:
+<div class="article">
+  <h2><a href="${article.href|h}">${article.title|h}</a></h2>
+  <p class="meta">written by <a href="${article.user.href|h
+    }">${article.user.username|h}</a> on ${dateformat(article.pub_date)}</p>
+  <div class="text">${article.body}</div>
+</div>
+% endfor
+<%call expr="form()">
+  <dl>
+    <dt>Name</dt>
+    <dd>${input_field('name')}</dd>
+    <dt>E-Mail</dt>
+    <dd>${input_field('email')}</dd>
+    <dt>URL</dt>
+    <dd>${input_field('url')}</dd>
+    <dt>Comment</dd>
+    <dd>${textarea('comment')}</dd>
+  </dl>
+  ${input_field(type='submit', value='Submit')}
+</%call>
diff --git a/examples/rwbench/mako/layout.html b/examples/rwbench/mako/layout.html
new file mode 100644 (file)
index 0000000..a9c353e
--- /dev/null
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+  <title>${self.page_title()} | RealWorld Benchmark</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+</head>
+<body>
+  <div class="contents">
+    <div class="header">
+      <h1>RealWorld Benchmark</h1>
+      <blockquote><p>
+        A less stupid benchmark for Mako and Jinja2 to get an impression how
+        code changes affect runtime performance.
+      </p></blockquote>
+    </div>
+    <ul class="navigation">
+    % for href, caption in page_navigation:
+      <li><a href="${href|h}">${caption}</a></li>
+    % endfor
+    </ul>
+    <div class="body">
+      ${self.body()}
+    </div>
+    <div class="footer">
+      &copy; Copyright 2008 by I don't know who.
+    </div>
+  </div>
+</body>
+</html>
+<%def name="page_title()"></%def>
diff --git a/examples/rwbench/rwbench.py b/examples/rwbench/rwbench.py
new file mode 100644 (file)
index 0000000..ccd144f
--- /dev/null
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+"""
+    RealWorldish Benchmark
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    A more real-world benchmark of Jinja2.
+
+    :copyright: Copyright 2008 by Armin Ronacher.
+    :license: BSD.
+"""
+import sys
+from os.path import join, dirname, abspath
+from random import choice, randrange
+from datetime import datetime
+from timeit import Timer
+from jinja2 import Environment, FileSystemLoader
+from jinja2.utils import generate_lorem_ipsum
+from mako.lookup import TemplateLookup
+
+
+ROOT = abspath(dirname(__file__))
+
+
+def dateformat(x):
+    return x.strftime('%Y-%m-%d')
+
+
+jinja_env = Environment(loader=FileSystemLoader(join(ROOT, 'jinja')))
+jinja_env.filters['dateformat'] = dateformat
+
+mako_lookup = TemplateLookup(directories=[join(ROOT, 'mako')])
+
+
+class Article(object):
+
+    def __init__(self, id):
+        self.id = id
+        self.href = '/article/%d' % self.id
+        self.title = generate_lorem_ipsum(1, False, 5, 10)
+        self.user = choice(users)
+        self.body = generate_lorem_ipsum()
+        self.pub_date = datetime.utcfromtimestamp(randrange(1000000000,
+                                                            2000000000))
+
+
+class User(object):
+
+    def __init__(self, username):
+        self.href = '/user/%s' % username
+        self.username = username
+
+
+users = map(User, [u'John Doe', u'Jane Doe', u'Peter Somewhat'])
+articles = map(Article, range(20))
+navigation = [
+    ('index',           'Index'),
+    ('about',           'About'),
+    ('foo?bar=1',       'Foo with Bar'),
+    ('foo?bar=2&s=x',   'Foo with X')
+]
+
+context = dict(users=users, articles=articles, page_navigation=navigation)
+
+
+jinja_template = jinja_env.get_template('index.html')
+mako_template = mako_lookup.get_template('index.html')
+
+
+def test_jinja():
+    jinja_template.render(context)
+
+def test_mako():
+    mako_template.render_unicode(**context)
+
+
+if __name__ == '__main__':
+    sys.stdout.write('Realworldish Benchmark:\n')
+    for test in 'jinja', 'mako':
+        t = Timer(setup='from __main__ import test_%s as bench' % test,
+                  stmt='bench()')
+        sys.stdout.write(' >> %-20s<running>' % test)
+        sys.stdout.flush()
+        sys.stdout.write('\r    %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
index 30054e73bd0f2ab4ddec01b2f1e29dd39a495015..2839264f514caf1939c7b89165f6493513032839 100644 (file)
@@ -273,6 +273,9 @@ class FrameIdentifierVisitor(NodeVisitor):
         if isinstance(node.arg, nodes.Const) and \
            isinstance(node.arg.value, basestring) and \
            ((isinstance(node.node, nodes.Name) and
+            # this code ignores parameter declared names as the may only
+            # occour at the very beginning of a scope and we pull the
+            # attributes afterwards.
             node.node.name not in (self.identifiers.declared_locally)) or
             node.node in self.identifiers.static_subscribes):
             if node in self.identifiers.static_subscribes:
@@ -281,7 +284,6 @@ class FrameIdentifierVisitor(NodeVisitor):
                 self.identifiers.static_subscribes[node] = 1
 
     def visit_Macro(self, node):
-        self.generic_visit(node)
         self.identifiers.declared_locally.add(node.name)
 
     def visit_Import(self, node):
@@ -665,7 +667,7 @@ class CodeGenerator(NodeVisitor):
                     self.writeline('import %s as %s' % (imp, alias))
 
         # add the load name
-        self.writeline('name = %r' % self.filename)
+        self.writeline('name = %r' % self.name)
 
         # generate the root render function.
         self.writeline('def root(context, environment=environment):', extra=1)
@@ -849,7 +851,7 @@ class CodeGenerator(NodeVisitor):
             self.writeline('if l_%s is missing:' % alias)
             self.indent()
             self.writeline('l_%s = environment.undefined(%r %% '
-                           'included_template.name, '
+                           'included_template.__name__, '
                            'name=%r)' %
                            (alias, 'the template %r does not export '
                             'the requested name ' + repr(name), name))
index d1206ef30615d4393c0444a1f4e8f011d109a334..f64b150f543ad8a79796bfa963cbd6261ac7a69b 100644 (file)
@@ -609,7 +609,7 @@ class TemplateModule(object):
         if self.__name__ is None:
             name = 'memory:%x' % id(self)
         else:
-            name = repr(self.name)
+            name = repr(self.__name__)
         return '<%s %s>' % (self.__class__.__name__, name)
 
 
index b5f0783e8246499ff26adf71e0c1adc6a46fa72a..17a599695178ba624e3cefa1e1116153e5c4116b 100644 (file)
@@ -21,10 +21,10 @@ __all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
            'markup_join', 'unicode_join']
 
 
-def markup_join(*args):
+def markup_join(seq):
     """Concatenation that escapes if necessary and converts to unicode."""
     buf = []
-    iterator = imap(soft_unicode, args)
+    iterator = imap(soft_unicode, seq)
     for arg in iterator:
         buf.append(arg)
         if hasattr(arg, '__html__'):
@@ -32,9 +32,9 @@ def markup_join(*args):
     return concat(buf)
 
 
-def unicode_join(*args):
+def unicode_join(seq):
     """Simple args to unicode conversion and concatenation."""
-    return concat(imap(unicode, args))
+    return concat(imap(unicode, seq))
 
 
 class Context(object):