blob: 473928b9c460e34882b3b86fcd71c3ddae7ead64 [file] [log] [blame]
Armin Ronacher32a910f2008-04-26 23:21:03 +02001"""\
David Lord57626a02019-10-23 12:29:46 -07002 This benchmark compares some python templating engines with Jinja so
3 that we get a picture of how fast Jinja 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 Ronacher32a910f2008-04-26 23:21:03 +02006import cgi
David Lordd177eeb2020-01-09 12:03:07 -08007import sys
Armin Ronacher00d5d212008-04-13 01:10:18 +02008from timeit import Timer
David Lordd177eeb2020-01-09 12:03:07 -08009
Armin Ronacher2feed1d2008-04-26 16:26:52 +020010from jinja2 import Environment as JinjaEnvironment
Armin Ronacher449167d2008-04-11 17:55:05 +020011
Armin Ronacherde6bf712008-04-26 01:44:14 +020012context = {
David Lord04c87872020-01-10 07:46:18 -080013 "page_title": "mitsuhiko's benchmark",
14 "table": [
15 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)
16 ],
Armin Ronacherde6bf712008-04-26 01:44:14 +020017}
Armin Ronacher449167d2008-04-11 17:55:05 +020018
Armin Ronacher00d5d212008-04-13 01:10:18 +020019jinja_template = JinjaEnvironment(
David Lord04c87872020-01-10 07:46:18 -080020 line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
21).from_string(
22 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +020023<!doctype html>
24<html>
25 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +020026 <title>${page_title|e}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +020027 </head>
28 <body>
29 <div class="header">
30 <h1>${page_title|e}</h1>
31 </div>
32 <ul class="navigation">
33 % for href, caption in [
34 ('index.html', 'Index'),
35 ('downloads.html', 'Downloads'),
36 ('products.html', 'Products')
37 ]
38 <li><a href="${href|e}">${caption|e}</a></li>
39 % endfor
40 </ul>
41 <div class="table">
42 <table>
43 % for row in table
44 <tr>
45 % for cell in row
46 <td>${cell}</td>
47 % endfor
48 </tr>
49 % endfor
50 </table>
51 </div>
52 </body>
53</html>\
David Lord04c87872020-01-10 07:46:18 -080054"""
55)
56
Armin Ronacher00d5d212008-04-13 01:10:18 +020057
Armin Ronacher2feed1d2008-04-26 16:26:52 +020058def test_jinja():
59 jinja_template.render(context)
60
David Lord04c87872020-01-10 07:46:18 -080061
Armin Ronacher2feed1d2008-04-26 16:26:52 +020062try:
Armin Ronacherb4da9be2009-09-10 13:58:52 -070063 from tornado.template import Template
64except ImportError:
65 test_tornado = None
66else:
David Lord04c87872020-01-10 07:46:18 -080067 tornado_template = Template(
68 """\
Armin Ronacherb4da9be2009-09-10 13:58:52 -070069<!doctype html>
70<html>
71 <head>
72 <title>{{ page_title }}</title>
73 </head>
74 <body>
75 <div class="header">
76 <h1>{{ page_title }}</h1>
77 </div>
78 <ul class="navigation">
79 {% for href, caption in [ \
80 ('index.html', 'Index'), \
81 ('downloads.html', 'Downloads'), \
82 ('products.html', 'Products') \
83 ] %}
84 <li><a href="{{ href }}">{{ caption }}</a></li>
85 {% end %}
86 </ul>
87 <div class="table">
88 <table>
89 {% for row in table %}
90 <tr>
91 {% for cell in row %}
92 <td>{{ cell }}</td>
93 {% end %}
94 </tr>
95 {% end %}
96 </table>
97 </div>
98 </body>
99</html>\
David Lord04c87872020-01-10 07:46:18 -0800100"""
101 )
Armin Ronacherb4da9be2009-09-10 13:58:52 -0700102
103 def test_tornado():
104 tornado_template.generate(**context)
105
David Lord04c87872020-01-10 07:46:18 -0800106
Armin Ronacherb4da9be2009-09-10 13:58:52 -0700107try:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200108 from django.conf import settings
David Lord04c87872020-01-10 07:46:18 -0800109
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200110 settings.configure()
111 from django.template import Template as DjangoTemplate, Context as DjangoContext
112except ImportError:
113 test_django = None
114else:
David Lord04c87872020-01-10 07:46:18 -0800115 django_template = DjangoTemplate(
116 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200117<!doctype html>
118<html>
119 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200120 <title>{{ page_title }}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200121 </head>
122 <body>
123 <div class="header">
124 <h1>{{ page_title }}</h1>
125 </div>
126 <ul class="navigation">
127 {% for href, caption in navigation %}
128 <li><a href="{{ href }}">{{ caption }}</a></li>
129 {% endfor %}
130 </ul>
131 <div class="table">
132 <table>
133 {% for row in table %}
134 <tr>
135 {% for cell in row %}
136 <td>{{ cell }}</td>
137 {% endfor %}
138 </tr>
139 {% endfor %}
140 </table>
141 </div>
142 </body>
143</html>\
David Lord04c87872020-01-10 07:46:18 -0800144"""
145 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200146
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200147 def test_django():
148 c = DjangoContext(context)
David Lord04c87872020-01-10 07:46:18 -0800149 c["navigation"] = [
150 ("index.html", "Index"),
151 ("downloads.html", "Downloads"),
152 ("products.html", "Products"),
153 ]
Armin Ronacherf60232d2010-06-05 14:31:27 +0200154 django_template.render(c)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200155
David Lord04c87872020-01-10 07:46:18 -0800156
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200157try:
158 from mako.template import Template as MakoTemplate
159except ImportError:
160 test_mako = None
161else:
David Lord04c87872020-01-10 07:46:18 -0800162 mako_template = MakoTemplate(
163 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200164<!doctype html>
165<html>
166 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200167 <title>${page_title|h}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200168 </head>
169 <body>
170 <div class="header">
171 <h1>${page_title|h}</h1>
172 </div>
173 <ul class="navigation">
174 % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
175 <li><a href="${href|h}">${caption|h}</a></li>
176 % endfor
177 </ul>
178 <div class="table">
179 <table>
180 % for row in table:
181 <tr>
182 % for cell in row:
183 <td>${cell}</td>
184 % endfor
185 </tr>
186 % endfor
187 </table>
188 </div>
189 </body>
190</html>\
David Lord04c87872020-01-10 07:46:18 -0800191"""
192 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200193
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200194 def test_mako():
195 mako_template.render(**context)
196
David Lord04c87872020-01-10 07:46:18 -0800197
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200198try:
199 from genshi.template import MarkupTemplate as GenshiTemplate
200except ImportError:
201 test_genshi = None
202else:
David Lord04c87872020-01-10 07:46:18 -0800203 genshi_template = GenshiTemplate(
204 """\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200205<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
206 <head>
207 <title>${page_title}</title>
208 </head>
209 <body>
210 <div class="header">
211 <h1>${page_title}</h1>
212 </div>
213 <ul class="navigation">
214 <li py:for="href, caption in [
215 ('index.html', 'Index'),
216 ('downloads.html', 'Downloads'),
217 ('products.html', 'Products')]"><a href="${href}">${caption}</a></li>
218 </ul>
219 <div class="table">
220 <table>
221 <tr py:for="row in table">
222 <td py:for="cell in row">${cell}</td>
223 </tr>
224 </table>
225 </div>
226 </body>
227</html>\
David Lord04c87872020-01-10 07:46:18 -0800228"""
229 )
Armin Ronacherde6bf712008-04-26 01:44:14 +0200230
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200231 def test_genshi():
David Lord04c87872020-01-10 07:46:18 -0800232 genshi_template.generate(**context).render("html", strip_whitespace=False)
233
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200234
235try:
236 from Cheetah.Template import Template as CheetahTemplate
237except ImportError:
238 test_cheetah = None
239else:
David Lord04c87872020-01-10 07:46:18 -0800240 cheetah_template = CheetahTemplate(
241 """\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200242#import cgi
243<!doctype html>
244<html>
245 <head>
246 <title>$cgi.escape($page_title)</title>
247 </head>
248 <body>
249 <div class="header">
250 <h1>$cgi.escape($page_title)</h1>
251 </div>
252 <ul class="navigation">
253 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
254 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
255 #end for
256 </ul>
257 <div class="table">
258 <table>
259 #for $row in $table:
260 <tr>
261 #for $cell in $row:
262 <td>$cell</td>
263 #end for
264 </tr>
265 #end for
266 </table>
267 </div>
268 </body>
269</html>\
David Lord04c87872020-01-10 07:46:18 -0800270""",
271 searchList=[dict(context)],
272 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200273
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200274 def test_cheetah():
275 unicode(cheetah_template)
Armin Ronacher00d5d212008-04-13 01:10:18 +0200276
David Lord04c87872020-01-10 07:46:18 -0800277
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200278try:
279 import tenjin
280except ImportError:
281 test_tenjin = None
282else:
283 tenjin_template = tenjin.Template()
David Lord04c87872020-01-10 07:46:18 -0800284 tenjin_template.convert(
285 """\
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200286<!doctype html>
287<html>
288 <head>
289 <title>${page_title}</title>
290 </head>
291 <body>
292 <div class="header">
293 <h1>${page_title}</h1>
294 </div>
295 <ul class="navigation">
296<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?>
297 <li><a href="${href}">${caption}</a></li>
298<?py #end ?>
299 </ul>
300 <div class="table">
301 <table>
302<?py for row in table: ?>
303 <tr>
304<?py for cell in row: ?>
305 <td>#{cell}</td>
306<?py #end ?>
307 </tr>
308<?py #end ?>
309 </table>
310 </div>
311 </body>
312</html>\
David Lord04c87872020-01-10 07:46:18 -0800313"""
314 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200315
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200316 def test_tenjin():
317 from tenjin.helpers import escape, to_str
David Lord04c87872020-01-10 07:46:18 -0800318
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200319 tenjin_template.render(context, locals())
Armin Ronacher00d5d212008-04-13 01:10:18 +0200320
David Lord04c87872020-01-10 07:46:18 -0800321
Armin Ronacher32a910f2008-04-26 23:21:03 +0200322try:
323 from spitfire.compiler import util as SpitfireTemplate
324 from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
325except ImportError:
326 test_spitfire = None
327else:
David Lord04c87872020-01-10 07:46:18 -0800328 spitfire_template = SpitfireTemplate.load_template(
329 """\
Armin Ronacher32a910f2008-04-26 23:21:03 +0200330<!doctype html>
331<html>
332 <head>
333 <title>$cgi.escape($page_title)</title>
334 </head>
335 <body>
336 <div class="header">
337 <h1>$cgi.escape($page_title)</h1>
338 </div>
339 <ul class="navigation">
340 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
341 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
342 #end for
343 </ul>
344 <div class="table">
345 <table>
346 #for $row in $table
347 <tr>
348 #for $cell in $row
349 <td>$cell</td>
350 #end for
351 </tr>
352 #end for
353 </table>
354 </div>
355 </body>
356</html>\
David Lord04c87872020-01-10 07:46:18 -0800357""",
358 "spitfire_tmpl",
359 spitfire_optimizer,
360 {"enable_filters": False},
361 )
362 spitfire_context = dict(context, **{"cgi": cgi})
Armin Ronacher32a910f2008-04-26 23:21:03 +0200363
364 def test_spitfire():
365 spitfire_template(search_list=[spitfire_context]).main()
366
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300367
368try:
369 from chameleon.zpt.template import PageTemplate
370except ImportError:
371 test_chameleon = None
372else:
David Lord04c87872020-01-10 07:46:18 -0800373 chameleon_template = PageTemplate(
374 """\
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300375<html xmlns:tal="http://xml.zope.org/namespaces/tal">
376 <head>
377 <title tal:content="page_title">Page Title</title>
378 </head>
379 <body>
380 <div class="header">
381 <h1 tal:content="page_title">Page Title</h1>
382 </div>
383 <ul class="navigation">
384 <li tal:repeat="item sections"><a tal:attributes="href item[0]" tal:content="item[1]">caption</a></li>
385 </ul>
386 <div class="table">
387 <table>
388 <tr tal:repeat="row table">
389 <td tal:repeat="cell row" tal:content="row[cell]">cell</td>
390 </tr>
391 </table>
392 </div>
393 </body>
394</html>\
David Lord04c87872020-01-10 07:46:18 -0800395"""
396 )
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300397 chameleon_context = dict(context)
David Lord04c87872020-01-10 07:46:18 -0800398 chameleon_context["sections"] = [
399 ("index.html", "Index"),
400 ("downloads.html", "Downloads"),
401 ("products.html", "Products"),
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300402 ]
David Lord04c87872020-01-10 07:46:18 -0800403
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300404 def test_chameleon():
405 chameleon_template.render(**chameleon_context)
406
David Lord04c87872020-01-10 07:46:18 -0800407
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300408try:
409 from chameleon.zpt.template import PageTemplate
410 from chameleon.genshi import language
411except ImportError:
412 test_chameleon_genshi = None
413else:
David Lord04c87872020-01-10 07:46:18 -0800414 chameleon_genshi_template = PageTemplate(
415 """\
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300416<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
417 <head>
418 <title>${page_title}</title>
419 </head>
420 <body>
421 <div class="header">
422 <h1>${page_title}</h1>
423 </div>
424 <ul class="navigation">
425 <li py:for="info in sections"><a href="${info[0]}">${info[1]}</a></li>
426 </ul>
427 <div class="table">
428 <table>
429 <tr py:for="row in table">
430 <td py:for="cell in row">${row[cell]}</td>
431 </tr>
432 </table>
433 </div>
434 </body>
435</html>\
David Lord04c87872020-01-10 07:46:18 -0800436""",
437 parser=language.Parser(),
438 )
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300439 chameleon_genshi_context = dict(context)
David Lord04c87872020-01-10 07:46:18 -0800440 chameleon_genshi_context["sections"] = [
441 ("index.html", "Index"),
442 ("downloads.html", "Downloads"),
443 ("products.html", "Products"),
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300444 ]
David Lord04c87872020-01-10 07:46:18 -0800445
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300446 def test_chameleon_genshi():
447 chameleon_genshi_template.render(**chameleon_genshi_context)
448
449
David Lord04c87872020-01-10 07:46:18 -0800450sys.stdout.write(
451 "\r"
452 + "\n".join(
453 (
454 "=" * 80,
455 "Template Engine BigTable Benchmark".center(80),
456 "=" * 80,
457 __doc__,
458 "-" * 80,
459 )
460 )
461 + "\n"
462)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200463
464
David Lord04c87872020-01-10 07:46:18 -0800465for test in (
466 "jinja",
467 "mako",
468 "tornado",
469 "tenjin",
470 "spitfire",
471 "django",
472 "genshi",
473 "cheetah",
474 "chameleon",
475 "chameleon_genshi",
476):
477 if locals()["test_" + test] is None:
478 sys.stdout.write(" %-20s*not installed*\n" % test)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200479 continue
David Lord04c87872020-01-10 07:46:18 -0800480 t = Timer(setup="from __main__ import test_%s as bench" % test, stmt="bench()")
481 sys.stdout.write(" >> %-20s<running>" % test)
Armin Ronacherde6bf712008-04-26 01:44:14 +0200482 sys.stdout.flush()
David Lord04c87872020-01-10 07:46:18 -0800483 sys.stdout.write("\r %-20s%.4f seconds\n" % (test, t.timeit(number=50) / 50))
484sys.stdout.write("-" * 80 + "\n")
485sys.stdout.write(
486 """\
Armin Ronacher32a910f2008-04-26 23:21:03 +0200487 WARNING: The results of this benchmark are useless to compare the
488 performance of template engines and should not be taken seriously in any
489 way. It's testing the performance of simple loops and has no real-world
490 usefulnes. It only used to check if changes on the Jinja code affect
491 performance in a good or bad way and how it roughly compares to others.
David Lord04c87872020-01-10 07:46:18 -0800492"""
493 + "=" * 80
494 + "\n"
495)