Down to 7 failures for Python 3. We're onto something.
[jinja2.git] / examples / bench.py
1 """\
2     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
4     template.  If a template engine is not installed the test is skipped.\
5 """
6 import sys
7 import cgi
8 from timeit import Timer
9 from jinja2 import Environment as JinjaEnvironment
10
11 context = {
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 }
15
16 jinja_template = JinjaEnvironment(
17     line_statement_prefix='%',
18     variable_start_string="${",
19     variable_end_string="}"
20 ).from_string("""\
21 <!doctype html>
22 <html>
23   <head>
24     <title>${page_title|e}</title>
25   </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>\
52 """)
53
54 def test_jinja():
55     jinja_template.render(context)
56
57 try:
58     from tornado.template import Template
59 except ImportError:
60     test_tornado = None
61 else:
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
99 try:
100     from django.conf import settings
101     settings.configure()
102     from django.template import Template as DjangoTemplate, Context as DjangoContext
103 except ImportError:
104     test_django = None
105 else:
106     django_template = """\
107 <!doctype html>
108 <html>
109   <head>
110     <title>{{ page_title }}</title>
111   </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>\
134 """
135
136     def test_django():
137         c = DjangoContext(context)
138         c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'),
139                            ('products.html', 'Products')]
140         # 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)
144
145 try:
146     from mako.template import Template as MakoTemplate
147 except ImportError:
148     test_mako = None
149 else:
150     mako_template = MakoTemplate("""\
151 <!doctype html>
152 <html>
153   <head>
154     <title>${page_title|h}</title>
155   </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
180     def test_mako():
181         mako_template.render(**context)
182
183 try:
184     from genshi.template import MarkupTemplate as GenshiTemplate
185 except ImportError:
186     test_genshi = None
187 else:
188     genshi_template = GenshiTemplate("""\
189 <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
214     def test_genshi():
215         genshi_template.generate(**context).render('html', strip_whitespace=False)
216
217 try:
218     from Cheetah.Template import Template as CheetahTemplate
219 except ImportError:
220     test_cheetah = None
221 else:
222     cheetah_template = CheetahTemplate("""\
223 #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)])
252
253     def test_cheetah():
254         unicode(cheetah_template)
255
256 try:
257     import tenjin
258 except ImportError:
259     test_tenjin = None
260 else:
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 """)
291
292     def test_tenjin():
293         from tenjin.helpers import escape, to_str
294         tenjin_template.render(context, locals())
295
296 try:
297     from spitfire.compiler import util as SpitfireTemplate
298     from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
299 except ImportError:
300     test_spitfire = None
301 else:
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
336 sys.stdout.write('\r' + '\n'.join((
337     '=' * 80,
338     'Template Engine BigTable Benchmark'.center(80),
339     '=' * 80,
340     __doc__,
341     '-' * 80
342 )) + '\n')
343
344
345 for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah':
346     if locals()['test_' + test] is None:
347         sys.stdout.write('    %-20s*not installed*\n' % test)
348         continue
349     t = Timer(setup='from __main__ import test_%s as bench' % test,
350               stmt='bench()')
351     sys.stdout.write(' >> %-20s<running>' % test)
352     sys.stdout.flush()
353     sys.stdout.write('\r    %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
354 sys.stdout.write('-' * 80 + '\n')
355 sys.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')