Catch down errors caused by tb_set_next. This fixes #22
[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 = DjangoTemplate("""\
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         django_template.render(c)
141
142 try:
143     from mako.template import Template as MakoTemplate
144 except ImportError:
145     test_mako = None
146 else:
147     mako_template = MakoTemplate("""\
148 <!doctype html>
149 <html>
150   <head>
151     <title>${page_title|h}</title>
152   </head>
153   <body>
154     <div class="header">
155       <h1>${page_title|h}</h1>
156     </div>
157     <ul class="navigation">
158     % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
159       <li><a href="${href|h}">${caption|h}</a></li>
160     % endfor
161     </ul>
162     <div class="table">
163       <table>
164       % for row in table:
165         <tr>
166         % for cell in row:
167           <td>${cell}</td>
168         % endfor
169         </tr>
170       % endfor
171       </table>
172     </div>
173   </body>
174 </html>\
175 """)
176
177     def test_mako():
178         mako_template.render(**context)
179
180 try:
181     from genshi.template import MarkupTemplate as GenshiTemplate
182 except ImportError:
183     test_genshi = None
184 else:
185     genshi_template = GenshiTemplate("""\
186 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
187   <head>
188     <title>${page_title}</title>
189   </head>
190   <body>
191     <div class="header">
192       <h1>${page_title}</h1>
193     </div>
194     <ul class="navigation">
195       <li py:for="href, caption in [
196         ('index.html', 'Index'),
197         ('downloads.html', 'Downloads'),
198         ('products.html', 'Products')]"><a href="${href}">${caption}</a></li>
199     </ul>
200     <div class="table">
201       <table>
202         <tr py:for="row in table">
203           <td py:for="cell in row">${cell}</td>
204         </tr>
205       </table>
206     </div>
207   </body>
208 </html>\
209 """)
210
211     def test_genshi():
212         genshi_template.generate(**context).render('html', strip_whitespace=False)
213
214 try:
215     from Cheetah.Template import Template as CheetahTemplate
216 except ImportError:
217     test_cheetah = None
218 else:
219     cheetah_template = CheetahTemplate("""\
220 #import cgi
221 <!doctype html>
222 <html>
223   <head>
224     <title>$cgi.escape($page_title)</title>
225   </head>
226   <body>
227     <div class="header">
228       <h1>$cgi.escape($page_title)</h1>
229     </div>
230     <ul class="navigation">
231     #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]:
232       <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
233     #end for
234     </ul>
235     <div class="table">
236       <table>
237       #for $row in $table:
238         <tr>
239         #for $cell in $row:
240           <td>$cell</td>
241         #end for
242         </tr>
243       #end for
244       </table>
245     </div>
246   </body>
247 </html>\
248 """, searchList=[dict(context)])
249
250     def test_cheetah():
251         unicode(cheetah_template)
252
253 try:
254     import tenjin
255 except ImportError:
256     test_tenjin = None
257 else:
258     tenjin_template = tenjin.Template()
259     tenjin_template.convert("""\
260 <!doctype html>
261 <html>
262   <head>
263     <title>${page_title}</title>
264   </head>
265   <body>
266     <div class="header">
267       <h1>${page_title}</h1>
268     </div>
269     <ul class="navigation">
270 <?py for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: ?>
271       <li><a href="${href}">${caption}</a></li>
272 <?py #end ?>
273     </ul>
274     <div class="table">
275       <table>
276 <?py for row in table: ?>
277         <tr>
278 <?py     for cell in row: ?>
279           <td>#{cell}</td>
280 <?py #end ?>
281         </tr>
282 <?py #end ?>
283       </table>
284     </div>
285   </body>
286 </html>\
287 """)
288
289     def test_tenjin():
290         from tenjin.helpers import escape, to_str
291         tenjin_template.render(context, locals())
292
293 try:
294     from spitfire.compiler import util as SpitfireTemplate
295     from spitfire.compiler.analyzer import o2_options as spitfire_optimizer
296 except ImportError:
297     test_spitfire = None
298 else:
299     spitfire_template = SpitfireTemplate.load_template("""\
300 <!doctype html>
301 <html>
302   <head>
303     <title>$cgi.escape($page_title)</title>
304   </head>
305   <body>
306     <div class="header">
307       <h1>$cgi.escape($page_title)</h1>
308     </div>
309     <ul class="navigation">
310     #for $href, $caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]
311       <li><a href="$cgi.escape($href)">$cgi.escape($caption)</a></li>
312     #end for
313     </ul>
314     <div class="table">
315       <table>
316       #for $row in $table
317         <tr>
318         #for $cell in $row
319           <td>$cell</td>
320         #end for
321         </tr>
322       #end for
323       </table>
324     </div>
325   </body>
326 </html>\
327 """, 'spitfire_tmpl', spitfire_optimizer, {'enable_filters': False})
328     spitfire_context = dict(context, **{'cgi': cgi})
329
330     def test_spitfire():
331         spitfire_template(search_list=[spitfire_context]).main()
332
333
334 try:
335     from chameleon.zpt.template import PageTemplate
336 except ImportError:
337     test_chameleon = None
338 else:
339     chameleon_template = PageTemplate("""\
340 <html xmlns:tal="http://xml.zope.org/namespaces/tal">
341   <head>
342     <title tal:content="page_title">Page Title</title>
343   </head>
344   <body>
345     <div class="header">
346       <h1 tal:content="page_title">Page Title</h1>
347     </div>
348     <ul class="navigation">
349     <li tal:repeat="item sections"><a tal:attributes="href item[0]" tal:content="item[1]">caption</a></li>
350     </ul>
351     <div class="table">
352       <table>
353         <tr tal:repeat="row table">
354         <td tal:repeat="cell row" tal:content="row[cell]">cell</td>
355         </tr>
356       </table>
357     </div>
358   </body>
359 </html>\
360 """)
361     chameleon_context = dict(context)
362     chameleon_context['sections'] = [
363         ('index.html', 'Index'),
364         ('downloads.html', 'Downloads'),
365         ('products.html', 'Products')
366     ]
367     def test_chameleon():
368         chameleon_template.render(**chameleon_context)
369
370 try:
371     from chameleon.zpt.template import PageTemplate
372     from chameleon.genshi import language
373 except ImportError:
374     test_chameleon_genshi = None
375 else:
376     chameleon_genshi_template = PageTemplate("""\
377 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
378   <head>
379     <title>${page_title}</title>
380   </head>
381   <body>
382     <div class="header">
383       <h1>${page_title}</h1>
384     </div>
385     <ul class="navigation">
386     <li py:for="info in sections"><a href="${info[0]}">${info[1]}</a></li>
387     </ul>
388     <div class="table">
389       <table>
390         <tr py:for="row in table">
391           <td py:for="cell in row">${row[cell]}</td>
392         </tr>
393       </table>
394     </div>
395   </body>
396 </html>\
397 """, parser=language.Parser())
398     chameleon_genshi_context = dict(context)
399     chameleon_genshi_context['sections'] = [
400         ('index.html', 'Index'),
401         ('downloads.html', 'Downloads'),
402         ('products.html', 'Products')
403     ]
404     def test_chameleon_genshi():
405         chameleon_genshi_template.render(**chameleon_genshi_context)
406
407
408 sys.stdout.write('\r' + '\n'.join((
409     '=' * 80,
410     'Template Engine BigTable Benchmark'.center(80),
411     '=' * 80,
412     __doc__,
413     '-' * 80
414 )) + '\n')
415
416
417 for test in 'jinja', 'mako', 'tornado', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah', 'chameleon', 'chameleon_genshi':
418     if locals()['test_' + test] is None:
419         sys.stdout.write('    %-20s*not installed*\n' % test)
420         continue
421     t = Timer(setup='from __main__ import test_%s as bench' % test,
422               stmt='bench()')
423     sys.stdout.write(' >> %-20s<running>' % test)
424     sys.stdout.flush()
425     sys.stdout.write('\r    %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
426 sys.stdout.write('-' * 80 + '\n')
427 sys.stdout.write('''\
428     WARNING: The results of this benchmark are useless to compare the
429     performance of template engines and should not be taken seriously in any
430     way.  It's testing the performance of simple loops and has no real-world
431     usefulnes.  It only used to check if changes on the Jinja code affect
432     performance in a good or bad way and how it roughly compares to others.
433 ''' + '=' * 80 + '\n')