blob: e610718f94702739fb2da2d663ec415db84e5d26 [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
David Lordbb6216e2020-01-10 10:40:52 -080011from jinja2._compat import text_type
Armin Ronacher449167d2008-04-11 17:55:05 +020012
Armin Ronacherde6bf712008-04-26 01:44:14 +020013context = {
David Lord04c87872020-01-10 07:46:18 -080014 "page_title": "mitsuhiko's benchmark",
15 "table": [
16 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)
17 ],
Armin Ronacherde6bf712008-04-26 01:44:14 +020018}
Armin Ronacher449167d2008-04-11 17:55:05 +020019
Armin Ronacher00d5d212008-04-13 01:10:18 +020020jinja_template = JinjaEnvironment(
David Lord04c87872020-01-10 07:46:18 -080021 line_statement_prefix="%", variable_start_string="${", variable_end_string="}"
22).from_string(
23 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +020024<!doctype html>
25<html>
26 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +020027 <title>${page_title|e}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +020028 </head>
29 <body>
30 <div class="header">
31 <h1>${page_title|e}</h1>
32 </div>
33 <ul class="navigation">
34 % for href, caption in [
35 ('index.html', 'Index'),
36 ('downloads.html', 'Downloads'),
37 ('products.html', 'Products')
38 ]
39 <li><a href="${href|e}">${caption|e}</a></li>
40 % endfor
41 </ul>
42 <div class="table">
43 <table>
44 % for row in table
45 <tr>
46 % for cell in row
47 <td>${cell}</td>
48 % endfor
49 </tr>
50 % endfor
51 </table>
52 </div>
53 </body>
54</html>\
David Lord04c87872020-01-10 07:46:18 -080055"""
56)
57
Armin Ronacher00d5d212008-04-13 01:10:18 +020058
Armin Ronacher2feed1d2008-04-26 16:26:52 +020059def test_jinja():
60 jinja_template.render(context)
61
David Lord04c87872020-01-10 07:46:18 -080062
Armin Ronacher2feed1d2008-04-26 16:26:52 +020063try:
Armin Ronacherb4da9be2009-09-10 13:58:52 -070064 from tornado.template import Template
65except ImportError:
66 test_tornado = None
67else:
David Lord04c87872020-01-10 07:46:18 -080068 tornado_template = Template(
69 """\
Armin Ronacherb4da9be2009-09-10 13:58:52 -070070<!doctype html>
71<html>
72 <head>
73 <title>{{ page_title }}</title>
74 </head>
75 <body>
76 <div class="header">
77 <h1>{{ page_title }}</h1>
78 </div>
79 <ul class="navigation">
80 {% for href, caption in [ \
81 ('index.html', 'Index'), \
82 ('downloads.html', 'Downloads'), \
83 ('products.html', 'Products') \
84 ] %}
85 <li><a href="{{ href }}">{{ caption }}</a></li>
86 {% end %}
87 </ul>
88 <div class="table">
89 <table>
90 {% for row in table %}
91 <tr>
92 {% for cell in row %}
93 <td>{{ cell }}</td>
94 {% end %}
95 </tr>
96 {% end %}
97 </table>
98 </div>
99 </body>
100</html>\
David Lord04c87872020-01-10 07:46:18 -0800101"""
102 )
Armin Ronacherb4da9be2009-09-10 13:58:52 -0700103
104 def test_tornado():
105 tornado_template.generate(**context)
106
David Lord04c87872020-01-10 07:46:18 -0800107
Armin Ronacherb4da9be2009-09-10 13:58:52 -0700108try:
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200109 from django.conf import settings
David Lord04c87872020-01-10 07:46:18 -0800110
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200111 settings.configure()
112 from django.template import Template as DjangoTemplate, Context as DjangoContext
113except ImportError:
114 test_django = None
115else:
David Lord04c87872020-01-10 07:46:18 -0800116 django_template = DjangoTemplate(
117 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200118<!doctype html>
119<html>
120 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200121 <title>{{ page_title }}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200122 </head>
123 <body>
124 <div class="header">
125 <h1>{{ page_title }}</h1>
126 </div>
127 <ul class="navigation">
128 {% for href, caption in navigation %}
129 <li><a href="{{ href }}">{{ caption }}</a></li>
130 {% endfor %}
131 </ul>
132 <div class="table">
133 <table>
134 {% for row in table %}
135 <tr>
136 {% for cell in row %}
137 <td>{{ cell }}</td>
138 {% endfor %}
139 </tr>
140 {% endfor %}
141 </table>
142 </div>
143 </body>
144</html>\
David Lord04c87872020-01-10 07:46:18 -0800145"""
146 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200147
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200148 def test_django():
149 c = DjangoContext(context)
David Lord04c87872020-01-10 07:46:18 -0800150 c["navigation"] = [
151 ("index.html", "Index"),
152 ("downloads.html", "Downloads"),
153 ("products.html", "Products"),
154 ]
Armin Ronacherf60232d2010-06-05 14:31:27 +0200155 django_template.render(c)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200156
David Lord04c87872020-01-10 07:46:18 -0800157
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200158try:
159 from mako.template import Template as MakoTemplate
160except ImportError:
161 test_mako = None
162else:
David Lord04c87872020-01-10 07:46:18 -0800163 mako_template = MakoTemplate(
164 """\
Armin Ronacher00d5d212008-04-13 01:10:18 +0200165<!doctype html>
166<html>
167 <head>
Armin Ronacherde6bf712008-04-26 01:44:14 +0200168 <title>${page_title|h}</title>
Armin Ronacher00d5d212008-04-13 01:10:18 +0200169 </head>
170 <body>
171 <div class="header">
172 <h1>${page_title|h}</h1>
173 </div>
174 <ul class="navigation">
David Lordbb6216e2020-01-10 10:40:52 -0800175 % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
176('products.html', 'Products')]:
Armin Ronacher00d5d212008-04-13 01:10:18 +0200177 <li><a href="${href|h}">${caption|h}</a></li>
178 % endfor
179 </ul>
180 <div class="table">
181 <table>
182 % for row in table:
183 <tr>
184 % for cell in row:
185 <td>${cell}</td>
186 % endfor
187 </tr>
188 % endfor
189 </table>
190 </div>
191 </body>
192</html>\
David Lord04c87872020-01-10 07:46:18 -0800193"""
194 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200195
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200196 def test_mako():
197 mako_template.render(**context)
198
David Lord04c87872020-01-10 07:46:18 -0800199
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200200try:
201 from genshi.template import MarkupTemplate as GenshiTemplate
202except ImportError:
203 test_genshi = None
204else:
David Lord04c87872020-01-10 07:46:18 -0800205 genshi_template = GenshiTemplate(
206 """\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200207<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
208 <head>
209 <title>${page_title}</title>
210 </head>
211 <body>
212 <div class="header">
213 <h1>${page_title}</h1>
214 </div>
215 <ul class="navigation">
216 <li py:for="href, caption in [
217 ('index.html', 'Index'),
218 ('downloads.html', 'Downloads'),
219 ('products.html', 'Products')]"><a href="${href}">${caption}</a></li>
220 </ul>
221 <div class="table">
222 <table>
223 <tr py:for="row in table">
224 <td py:for="cell in row">${cell}</td>
225 </tr>
226 </table>
227 </div>
228 </body>
229</html>\
David Lord04c87872020-01-10 07:46:18 -0800230"""
231 )
Armin Ronacherde6bf712008-04-26 01:44:14 +0200232
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200233 def test_genshi():
David Lord04c87872020-01-10 07:46:18 -0800234 genshi_template.generate(**context).render("html", strip_whitespace=False)
235
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200236
237try:
238 from Cheetah.Template import Template as CheetahTemplate
239except ImportError:
240 test_cheetah = None
241else:
David Lord04c87872020-01-10 07:46:18 -0800242 cheetah_template = CheetahTemplate(
243 """\
Armin Ronacherde6bf712008-04-26 01:44:14 +0200244#import cgi
245<!doctype html>
246<html>
247 <head>
248 <title>$cgi.escape($page_title)</title>
249 </head>
250 <body>
251 <div class="header">
252 <h1>$cgi.escape($page_title)</h1>
253 </div>
254 <ul class="navigation">
David Lordbb6216e2020-01-10 10:40:52 -0800255 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
256('products.html', 'Products')]:
Armin Ronacherde6bf712008-04-26 01:44:14 +0200257 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
258 #end for
259 </ul>
260 <div class="table">
261 <table>
262 #for $row in $table:
263 <tr>
264 #for $cell in $row:
265 <td>$cell</td>
266 #end for
267 </tr>
268 #end for
269 </table>
270 </div>
271 </body>
272</html>\
David Lord04c87872020-01-10 07:46:18 -0800273""",
274 searchList=[dict(context)],
275 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200276
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200277 def test_cheetah():
David Lordbb6216e2020-01-10 10:40:52 -0800278 text_type(cheetah_template)
Armin Ronacher00d5d212008-04-13 01:10:18 +0200279
David Lord04c87872020-01-10 07:46:18 -0800280
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200281try:
282 import tenjin
283except ImportError:
284 test_tenjin = None
285else:
286 tenjin_template = tenjin.Template()
David Lord04c87872020-01-10 07:46:18 -0800287 tenjin_template.convert(
288 """\
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200289<!doctype html>
290<html>
291 <head>
292 <title>${page_title}</title>
293 </head>
294 <body>
295 <div class="header">
296 <h1>${page_title}</h1>
297 </div>
298 <ul class="navigation">
David Lordbb6216e2020-01-10 10:40:52 -0800299<?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
300('products.html', 'Products')]: ?>
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200301 <li><a href="${href}">${caption}</a></li>
302<?py #end ?>
303 </ul>
304 <div class="table">
305 <table>
306<?py for row in table: ?>
307 <tr>
308<?py for cell in row: ?>
309 <td>#{cell}</td>
310<?py #end ?>
311 </tr>
312<?py #end ?>
313 </table>
314 </div>
315 </body>
316</html>\
David Lord04c87872020-01-10 07:46:18 -0800317"""
318 )
Armin Ronacher00d5d212008-04-13 01:10:18 +0200319
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200320 def test_tenjin():
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200321 tenjin_template.render(context, locals())
Armin Ronacher00d5d212008-04-13 01:10:18 +0200322
David Lord04c87872020-01-10 07:46:18 -0800323
Armin Ronacher32a910f2008-04-26 23:21:03 +0200324try:
325 from spitfire.compiler import util as SpitfireTemplate
326 from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
327except ImportError:
328 test_spitfire = None
329else:
David Lord04c87872020-01-10 07:46:18 -0800330 spitfire_template = SpitfireTemplate.load_template(
331 """\
Armin Ronacher32a910f2008-04-26 23:21:03 +0200332<!doctype html>
333<html>
334 <head>
335 <title>$cgi.escape($page_title)</title>
336 </head>
337 <body>
338 <div class="header">
339 <h1>$cgi.escape($page_title)</h1>
340 </div>
341 <ul class="navigation">
David Lordbb6216e2020-01-10 10:40:52 -0800342 #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), \
343('products.html', 'Products')]
Armin Ronacher32a910f2008-04-26 23:21:03 +0200344 <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
345 #end for
346 </ul>
347 <div class="table">
348 <table>
349 #for $row in $table
350 <tr>
351 #for $cell in $row
352 <td>$cell</td>
353 #end for
354 </tr>
355 #end for
356 </table>
357 </div>
358 </body>
359</html>\
David Lord04c87872020-01-10 07:46:18 -0800360""",
361 "spitfire_tmpl",
362 spitfire_optimizer,
363 {"enable_filters": False},
364 )
365 spitfire_context = dict(context, **{"cgi": cgi})
Armin Ronacher32a910f2008-04-26 23:21:03 +0200366
367 def test_spitfire():
368 spitfire_template(search_list=[spitfire_context]).main()
369
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300370
371try:
372 from chameleon.zpt.template import PageTemplate
373except ImportError:
374 test_chameleon = None
375else:
David Lord04c87872020-01-10 07:46:18 -0800376 chameleon_template = PageTemplate(
377 """\
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300378<html xmlns:tal="http://xml.zope.org/namespaces/tal">
379 <head>
380 <title tal:content="page_title">Page Title</title>
381 </head>
382 <body>
383 <div class="header">
384 <h1 tal:content="page_title">Page Title</h1>
385 </div>
386 <ul class="navigation">
David Lordbb6216e2020-01-10 10:40:52 -0800387 <li tal:repeat="item sections"><a tal:attributes="href item[0]" \
388tal:content="item[1]">caption</a></li>
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300389 </ul>
390 <div class="table">
391 <table>
392 <tr tal:repeat="row table">
393 <td tal:repeat="cell row" tal:content="row[cell]">cell</td>
394 </tr>
395 </table>
396 </div>
397 </body>
398</html>\
David Lord04c87872020-01-10 07:46:18 -0800399"""
400 )
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300401 chameleon_context = dict(context)
David Lord04c87872020-01-10 07:46:18 -0800402 chameleon_context["sections"] = [
403 ("index.html", "Index"),
404 ("downloads.html", "Downloads"),
405 ("products.html", "Products"),
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300406 ]
David Lord04c87872020-01-10 07:46:18 -0800407
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300408 def test_chameleon():
409 chameleon_template.render(**chameleon_context)
410
David Lord04c87872020-01-10 07:46:18 -0800411
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300412try:
413 from chameleon.zpt.template import PageTemplate
414 from chameleon.genshi import language
415except ImportError:
416 test_chameleon_genshi = None
417else:
David Lord04c87872020-01-10 07:46:18 -0800418 chameleon_genshi_template = PageTemplate(
419 """\
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300420<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
421 <head>
422 <title>${page_title}</title>
423 </head>
424 <body>
425 <div class="header">
426 <h1>${page_title}</h1>
427 </div>
428 <ul class="navigation">
429 <li py:for="info in sections"><a href="${info[0]}">${info[1]}</a></li>
430 </ul>
431 <div class="table">
432 <table>
433 <tr py:for="row in table">
434 <td py:for="cell in row">${row[cell]}</td>
435 </tr>
436 </table>
437 </div>
438 </body>
439</html>\
David Lord04c87872020-01-10 07:46:18 -0800440""",
441 parser=language.Parser(),
442 )
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300443 chameleon_genshi_context = dict(context)
David Lord04c87872020-01-10 07:46:18 -0800444 chameleon_genshi_context["sections"] = [
445 ("index.html", "Index"),
446 ("downloads.html", "Downloads"),
447 ("products.html", "Products"),
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300448 ]
David Lord04c87872020-01-10 07:46:18 -0800449
Rodrigo Moraes6cc2b232010-08-17 12:08:01 -0300450 def test_chameleon_genshi():
451 chameleon_genshi_template.render(**chameleon_genshi_context)
452
453
David Lord04c87872020-01-10 07:46:18 -0800454sys.stdout.write(
455 "\r"
456 + "\n".join(
457 (
458 "=" * 80,
459 "Template Engine BigTable Benchmark".center(80),
460 "=" * 80,
461 __doc__,
462 "-" * 80,
463 )
464 )
465 + "\n"
466)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200467
468
David Lord04c87872020-01-10 07:46:18 -0800469for test in (
470 "jinja",
471 "mako",
472 "tornado",
473 "tenjin",
474 "spitfire",
475 "django",
476 "genshi",
477 "cheetah",
478 "chameleon",
479 "chameleon_genshi",
480):
481 if locals()["test_" + test] is None:
482 sys.stdout.write(" %-20s*not installed*\n" % test)
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200483 continue
David Lord04c87872020-01-10 07:46:18 -0800484 t = Timer(setup="from __main__ import test_%s as bench" % test, stmt="bench()")
485 sys.stdout.write(" >> %-20s<running>" % test)
Armin Ronacherde6bf712008-04-26 01:44:14 +0200486 sys.stdout.flush()
David Lord04c87872020-01-10 07:46:18 -0800487 sys.stdout.write("\r %-20s%.4f seconds\n" % (test, t.timeit(number=50) / 50))
488sys.stdout.write("-" * 80 + "\n")
489sys.stdout.write(
490 """\
Armin Ronacher32a910f2008-04-26 23:21:03 +0200491 WARNING: The results of this benchmark are useless to compare the
492 performance of template engines and should not be taken seriously in any
493 way. It's testing the performance of simple loops and has no real-world
494 usefulnes. It only used to check if changes on the Jinja code affect
495 performance in a good or bad way and how it roughly compares to others.
David Lord04c87872020-01-10 07:46:18 -0800496"""
497 + "=" * 80
498 + "\n"
499)