From dc02b64d62e1871a619cac2dab42d041755c3686 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 15 May 2008 22:47:27 +0200 Subject: [PATCH] added a semi realworld benchmark (jinja2 and mako) --HG-- branch : trunk --- examples/rwbench/jinja/helpers.html | 12 +++++ examples/rwbench/jinja/index.html | 26 +++++++++ examples/rwbench/jinja/layout.html | 29 ++++++++++ examples/rwbench/mako/helpers.html | 11 ++++ examples/rwbench/mako/index.html | 27 ++++++++++ examples/rwbench/mako/layout.html | 30 +++++++++++ examples/rwbench/rwbench.py | 83 +++++++++++++++++++++++++++++ jinja2/compiler.py | 8 +-- jinja2/environment.py | 2 +- jinja2/runtime.py | 8 +-- 10 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 examples/rwbench/jinja/helpers.html create mode 100644 examples/rwbench/jinja/index.html create mode 100644 examples/rwbench/jinja/layout.html create mode 100644 examples/rwbench/mako/helpers.html create mode 100644 examples/rwbench/mako/index.html create mode 100644 examples/rwbench/mako/layout.html create mode 100644 examples/rwbench/rwbench.py diff --git a/examples/rwbench/jinja/helpers.html b/examples/rwbench/jinja/helpers.html new file mode 100644 index 0000000..89976aa --- /dev/null +++ b/examples/rwbench/jinja/helpers.html @@ -0,0 +1,12 @@ +{% macro input_field(name, value='', type='text') -%} + +{%- endmacro %} + +{% macro textarea(name, value='', rows=10, cols=40) -%} + +{%- endmacro %} + +{% macro form(action='', method='post') -%} +
{{ caller() }}
+{%- endmacro %} diff --git a/examples/rwbench/jinja/index.html b/examples/rwbench/jinja/index.html new file mode 100644 index 0000000..2b97e70 --- /dev/null +++ b/examples/rwbench/jinja/index.html @@ -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 %} +
+

{{ article.title|e }}

+

written by {{ article.user.username|e }} on {{ article.pub_date|dateformat }}

+
{{ article.body }}
+
+ {%- endfor %} + {%- call form() %} +
+
Name
+
{{ input_field('name') }}
+
E-Mail
+
{{ input_field('email') }}
+
URL
+
{{ input_field('url') }}
+
Comment +
{{ textarea('comment') }}
+
+ {{ 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 index 0000000..755789e --- /dev/null +++ b/examples/rwbench/jinja/layout.html @@ -0,0 +1,29 @@ + + + + {% block page_title %}{% endblock %} | RealWorld Benchmark + + + +
+
+

RealWorld Benchmark

+

+ A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. +

+
+ +
+ {% block body %}{% endblock %} +
+ +
+ + diff --git a/examples/rwbench/mako/helpers.html b/examples/rwbench/mako/helpers.html new file mode 100644 index 0000000..a0290eb --- /dev/null +++ b/examples/rwbench/mako/helpers.html @@ -0,0 +1,11 @@ +<%def name="input_field(name='', value='', type='text')"> + + + +<%def name="textarea(name, value='', rows=10, cols=40)"> + + + +<%def name="form(action='', method='post')"> +
${caller.body()}
+ diff --git a/examples/rwbench/mako/index.html b/examples/rwbench/mako/index.html new file mode 100644 index 0000000..33bfe32 --- /dev/null +++ b/examples/rwbench/mako/index.html @@ -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 +% for article in articles: +
+

${article.title|h}

+

written by ${article.user.username|h} on ${dateformat(article.pub_date)}

+
${article.body}
+
+% endfor +<%call expr="form()"> +
+
Name
+
${input_field('name')}
+
E-Mail
+
${input_field('email')}
+
URL
+
${input_field('url')}
+
Comment +
${textarea('comment')}
+
+ ${input_field(type='submit', value='Submit')} + diff --git a/examples/rwbench/mako/layout.html b/examples/rwbench/mako/layout.html new file mode 100644 index 0000000..a9c353e --- /dev/null +++ b/examples/rwbench/mako/layout.html @@ -0,0 +1,30 @@ + + + + ${self.page_title()} | RealWorld Benchmark + + + +
+
+

RealWorld Benchmark

+

+ A less stupid benchmark for Mako and Jinja2 to get an impression how + code changes affect runtime performance. +

+
+ +
+ ${self.body()} +
+ +
+ + +<%def name="page_title()"> diff --git a/examples/rwbench/rwbench.py b/examples/rwbench/rwbench.py new file mode 100644 index 0000000..ccd144f --- /dev/null +++ b/examples/rwbench/rwbench.py @@ -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' % test) + sys.stdout.flush() + sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50)) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index 30054e7..2839264 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -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)) diff --git a/jinja2/environment.py b/jinja2/environment.py index d1206ef..f64b150 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -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) diff --git a/jinja2/runtime.py b/jinja2/runtime.py index b5f0783..17a5996 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -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): -- 2.26.2