blob: 0d5a68fef62166b14feb892020abba9dc626be6d [file] [log] [blame]
Armin Ronacher32a910f2008-04-26 23:21:03 +02001"""\
Armin Ronacher2feed1d2008-04-26 16:26:52 +02002 This benchmark compares some python templating engines with Jinja 2 so
3 that we get a picture of how fast Jinja 2 is for a semi real world
Armin Ronacher32a910f2008-04-26 23:21:03 +02004 template. If a template engine is not installed the test is skipped.\
Armin Ronacher2feed1d2008-04-26 16:26:52 +02005"""
Armin Ronacherde6bf712008-04-26 01:44:14 +02006import sys
Armin Ronacher32a910f2008-04-26 23:21:03 +02007import cgi
Armin Ronacher00d5d212008-04-13 01:10:18 +02008from timeit import Timer
Armin Ronacher2feed1d2008-04-26 16:26:52 +02009from jinja2 import Environment as JinjaEnvironment
Armin Ronacher449167d2008-04-11 17:55:05 +020010
Armin Ronacherde6bf712008-04-26 01:44:14 +020011context = {
12 'page_title': 'mitsuhiko\'s benchmark',
13 '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)]
14}
Armin Ronacher449167d2008-04-11 17:55:05 +020015
Armin Ronacher00d5d212008-04-13 01:10:18 +020016jinja_template = JinjaEnvironment(
17 line_statement_prefix='%',
18 variable_start_string="${",
19 variable_end_string="}"
20).from_string("""\
21<!doctype html>
22<html>
23 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +020024 <title>${page_title|e}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +020025 </head>
26 <body>
27 <div class="header">
28 <h1>${page_title|e}</h1>
29 </div>
30 <ul class="navigation">
31 % for href, caption in [
32 ('index.html', 'Index'),
33 ('downloads.html', 'Downloads'),
34 ('products.html', 'Products')
35 ]
36 <li><a href="${href|e}">${caption|e}</a></li>
37 % endfor
38 </ul>
39 <div class="table">
40 <table>
41 % for row in table
42 <tr>
43 % for cell in row
44 <td>${cell}</td>
45 % endfor
46 </tr>
47 % endfor
48 </table>
49 </div>
50 </body>
51</html>\
Armin Ronacher449167d2008-04-11 17:55:05 +020052""")
Armin Ronacher00d5d212008-04-13 01:10:18 +020053
Armin Ronacher2feed1d2008-04-26 16:26:52 +020054def test_jinja():
55 jinja_template.render(context)
56
57try:
Armin Ronacherb4da9be2009-09-10 13:58:52 -070058 from tornado.template import Template
59except ImportError:
60 test_tornado = None
61else:
62 tornado_template = Template("""\
63<!doctype html>
64<html>
65 <head>
66 <title>{{ page_title }}</title>
67 </head>
68 <body>
69 <div class="header">
70 <h1>{{ page_title }}</h1>
71 </div>
72 <ul class="navigation">
73 {% for href, caption in [ \
74 ('index.html', 'Index'), \
75 ('downloads.html', 'Downloads'), \
76 ('products.html', 'Products') \
77 ] %}
78 <li><a href="{{ href }}">{{ caption }}</a></li>
79 {% end %}
80 </ul>
81 <div class="table">
82 <table>
83 {% for row in table %}
84 <tr>
85 {% for cell in row %}
86 <td>{{ cell }}</td>
87 {% end %}
88 </tr>
89 {% end %}
90 </table>
91 </div>
92 </body>
93</html>\
94""")
95
96 def test_tornado():
97 tornado_template.generate(**context)
98
99try:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200100 from django.conf import settings
101 settings.configure()
102 from django.template import Template as DjangoTemplate, Context as DjangoContext
103except ImportError:
104 test_django = None
105else:
Armin Ronacherc347ed02008-09-20 12:04:53 +0200106 django_template = """\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200107<!doctype html>
108<html>
109 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200110 <title>{{ page_title }}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200111 </head>
112 <body>
113 <div class="header">
114 <h1>{{ page_title }}</h1>
115 </div>
116 <ul class="navigation">
117 {% for href, caption in navigation %}
118 <li><a href="{{ href }}">{{ caption }}</a></li>
119 {% endfor %}
120 </ul>
121 <div class="table">
122 <table>
123 {% for row in table %}
124 <tr>
125 {% for cell in row %}
126 <td>{{ cell }}</td>
127 {% endfor %}
128 </tr>
129 {% endfor %}
130 </table>
131 </div>
132 </body>
133</html>\
Armin Ronacherc347ed02008-09-20 12:04:53 +0200134"""
Armin Ronacher00d5d212008-04-13 01:10:18 +0200135
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200136 def test_django():
137 c = DjangoContext(context)
138 c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'),
139 ('products.html', 'Products')]
Armin Ronacherc347ed02008-09-20 12:04:53 +0200140 # recompile template each rendering because that's what django
141 # is doing in normal situations too. Django is not thread safe
142 # so we can't cache it in regular apps either.
143 DjangoTemplate(django_template).render(c)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200144
145try:
146 from mako.template import Template as MakoTemplate
147except ImportError:
148 test_mako = None
149else:
150 mako_template = MakoTemplate("""\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200151<!doctype html>
152<html>
153 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200154 <title>${page_title|h}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200155 </head>
156 <body>
157 <div class="header">
158 <h1>${page_title|h}</h1>
159 </div>
160 <ul class="navigation">
161 % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
162 <li><a href="${href|h}">${caption|h}</a></li>
163 % endfor
164 </ul>
165 <div class="table">
166 <table>
167 % for row in table:
168 <tr>
169 % for cell in row:
170 <td>${cell}</td>
171 % endfor
172 </tr>
173 % endfor
174 </table>
175 </div>
176 </body>
177</html>\
178""")
179
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200180 def test_mako():
181 mako_template.render(**context)
182
183try:
184 from genshi.template import MarkupTemplate as GenshiTemplate
185except ImportError:
186 test_genshi = None
187else:
188 genshi_template = GenshiTemplate("""\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200189<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
190 <head>
191 <title>${page_title}</title>
192 </head>
193 <body>
194 <div class="header">
195 <h1>${page_title}</h1>
196 </div>
197 <ul class="navigation">
198 <li py:for="href, caption in [
199 ('index.html', 'Index'),
200 ('downloads.html', 'Downloads'),
201 ('products.html', 'Products')]"><a href="${href}">${caption}</a></li>
202 </ul>
203 <div class="table">
204 <table>
205 <tr py:for="row in table">
206 <td py:for="cell in row">${cell}</td>
207 </tr>
208 </table>
209 </div>
210 </body>
211</html>\
212""")
213
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200214 def test_genshi():
215 genshi_template.generate(**context).render('html', strip_whitespace=False)
216
217try:
218 from Cheetah.Template import Template as CheetahTemplate
219except ImportError:
220 test_cheetah = None
221else:
222 cheetah_template = CheetahTemplate("""\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200223#import cgi
224<!doctype html>
225<html>
226 <head>
227 <title>$cgi.escape($page_title)</title>
228 </head>
229 <body>
230 <div class="header">
231 <h1>$cgi.escape($page_title)</h1>
232 </div>
233 <ul class="navigation">
234 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
235 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
236 #end for
237 </ul>
238 <div class="table">
239 <table>
240 #for $row in $table:
241 <tr>
242 #for $cell in $row:
243 <td>$cell</td>
244 #end for
245 </tr>
246 #end for
247 </table>
248 </div>
249 </body>
250</html>\
251""", searchList=[dict(context)])
Armin Ronacher00d5d212008-04-13 01:10:18 +0200252
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200253 def test_cheetah():
254 unicode(cheetah_template)
Armin Ronacher00d5d212008-04-13 01:10:18 +0200255
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200256try:
257 import tenjin
258except ImportError:
259 test_tenjin = None
260else:
261 tenjin_template = tenjin.Template()
262 tenjin_template.convert("""\
263<!doctype html>
264<html>
265 <head>
266 <title>${page_title}</title>
267 </head>
268 <body>
269 <div class="header">
270 <h1>${page_title}</h1>
271 </div>
272 <ul class="navigation">
273<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?>
274 <li><a href="${href}">${caption}</a></li>
275<?py #end ?>
276 </ul>
277 <div class="table">
278 <table>
279<?py for row in table: ?>
280 <tr>
281<?py for cell in row: ?>
282 <td>#{cell}</td>
283<?py #end ?>
284 </tr>
285<?py #end ?>
286 </table>
287 </div>
288 </body>
289</html>\
290""")
Armin Ronacher00d5d212008-04-13 01:10:18 +0200291
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200292 def test_tenjin():
293 from tenjin.helpers import escape, to_str
294 tenjin_template.render(context, locals())
Armin Ronacher00d5d212008-04-13 01:10:18 +0200295
Armin Ronacher32a910f2008-04-26 23:21:03 +0200296try:
297 from spitfire.compiler import util as SpitfireTemplate
298 from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
299except ImportError:
300 test_spitfire = None
301else:
302 spitfire_template = SpitfireTemplate.load_template("""\
303<!doctype html>
304<html>
305 <head>
306 <title>$cgi.escape($page_title)</title>
307 </head>
308 <body>
309 <div class="header">
310 <h1>$cgi.escape($page_title)</h1>
311 </div>
312 <ul class="navigation">
313 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
314 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
315 #end for
316 </ul>
317 <div class="table">
318 <table>
319 #for $row in $table
320 <tr>
321 #for $cell in $row
322 <td>$cell</td>
323 #end for
324 </tr>
325 #end for
326 </table>
327 </div>
328 </body>
329</html>\
330""", 'spitfire_tmpl', spitfire_optimizer, {'enable_filters': False})
331 spitfire_context = dict(context, **{'cgi': cgi})
332
333 def test_spitfire():
334 spitfire_template(search_list=[spitfire_context]).main()
335
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200336sys.stdout.write('\r' + '\n'.join((
Armin Ronacherde6bf712008-04-26 01:44:14 +0200337 '=' * 80,
338 'Template Engine BigTable Benchmark'.center(80),
Armin Ronacher32a910f2008-04-26 23:21:03 +0200339 '=' * 80,
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200340 __doc__,
Armin Ronacherde6bf712008-04-26 01:44:14 +0200341 '-' * 80
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200342)) + '\n')
Armin Ronacherc9705c22008-04-27 21:28:03 +0200343
344
Armin Ronacherb4da9be2009-09-10 13:58:52 -0700345for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah':
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200346 if locals()['test_' + test] is None:
Armin Ronacher32a910f2008-04-26 23:21:03 +0200347 sys.stdout.write(' %-20s*not installed*\n' % test)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200348 continue
Armin Ronacher00d5d212008-04-13 01:10:18 +0200349 t = Timer(setup='from __main__ import test_%s as bench' % test,
350 stmt='bench()')
Armin Ronacher32a910f2008-04-26 23:21:03 +0200351 sys.stdout.write(' >> %-20s<running>' % test)
Armin Ronacherde6bf712008-04-26 01:44:14 +0200352 sys.stdout.flush()
Armin Ronacher19cf9c22008-05-01 12:49:53 +0200353 sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
Armin Ronacher32a910f2008-04-26 23:21:03 +0200354sys.stdout.write('-' * 80 + '\n')
355sys.stdout.write('''\
356 WARNING: The results of this benchmark are useless to compare the
357 performance of template engines and should not be taken seriously in any
358 way. It's testing the performance of simple loops and has no real-world
359 usefulnes. It only used to check if changes on the Jinja code affect
360 performance in a good or bad way and how it roughly compares to others.
361''' + '=' * 80 + '\n')