1# basic.py - basic benchmarks adapted from Genshi
2# Copyright (C) 2006 Edgewall Software
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions
7# are met:
8#
9#  1. Redistributions of source code must retain the above copyright
10#     notice, this list of conditions and the following disclaimer.
11#  2. Redistributions in binary form must reproduce the above copyright
12#     notice, this list of conditions and the following disclaimer in
13#     the documentation and/or other materials provided with the
14#     distribution.
15#  3. The name of the author may not be used to endorse or promote
16#     products derived from this software without specific prior
17#     written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
29# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31from io import StringIO
32import sys
33import timeit
34
35
36__all__ = [
37    "mako",
38    "mako_inheritance",
39    "jinja2",
40    "jinja2_inheritance",
41    "cheetah",
42    "django",
43    "myghty",
44    "genshi",
45    "kid",
46]
47
48# Templates content and constants
49TITLE = "Just a test"
50USER = "joe"
51ITEMS = ["Number %d" % num for num in range(1, 15)]
52
53
54def genshi(dirname, verbose=False):
55    from genshi.template import TemplateLoader
56
57    loader = TemplateLoader([dirname], auto_reload=False)
58    template = loader.load("template.html")
59
60    def render():
61        data = dict(title=TITLE, user=USER, items=ITEMS)
62        return template.generate(**data).render("xhtml")
63
64    if verbose:
65        print(render())
66    return render
67
68
69def myghty(dirname, verbose=False):
70    from myghty import interp
71
72    interpreter = interp.Interpreter(component_root=dirname)
73
74    def render():
75        data = dict(title=TITLE, user=USER, items=ITEMS)
76        buffer = StringIO()
77        interpreter.execute(
78            "template.myt", request_args=data, out_buffer=buffer
79        )
80        return buffer.getvalue()
81
82    if verbose:
83        print(render())
84    return render
85
86
87def mako(dirname, verbose=False):
88    from mako.template import Template
89    from mako.lookup import TemplateLookup
90
91    lookup = TemplateLookup(directories=[dirname], filesystem_checks=False)
92    template = lookup.get_template("template.html")
93
94    def render():
95        return template.render(title=TITLE, user=USER, list_items=ITEMS)
96
97    if verbose:
98        print(template.code + " " + render())
99    return render
100
101
102mako_inheritance = mako
103
104
105def jinja2(dirname, verbose=False):
106    from jinja2 import Environment, FileSystemLoader
107
108    env = Environment(loader=FileSystemLoader(dirname))
109    template = env.get_template("template.html")
110
111    def render():
112        return template.render(title=TITLE, user=USER, list_items=ITEMS)
113
114    if verbose:
115        print(render())
116    return render
117
118
119jinja2_inheritance = jinja2
120
121
122def cheetah(dirname, verbose=False):
123    from Cheetah.Template import Template
124
125    filename = os.path.join(dirname, "template.tmpl")
126    template = Template(file=filename)
127
128    def render():
129        template.__dict__.update(
130            {"title": TITLE, "user": USER, "list_items": ITEMS}
131        )
132        return template.respond()
133
134    if verbose:
135        print(dir(template))
136        print(template.generatedModuleCode())
137        print(render())
138    return render
139
140
141def django(dirname, verbose=False):
142    from django.conf import settings
143
144    settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, "templates")])
145    from django import template, templatetags
146    from django.template import loader
147
148    templatetags.__path__.append(os.path.join(dirname, "templatetags"))
149    tmpl = loader.get_template("template.html")
150
151    def render():
152        data = {"title": TITLE, "user": USER, "items": ITEMS}
153        return tmpl.render(template.Context(data))
154
155    if verbose:
156        print(render())
157    return render
158
159
160def kid(dirname, verbose=False):
161    import kid
162
163    kid.path = kid.TemplatePath([dirname])
164    template = kid.Template(file="template.kid")
165
166    def render():
167        template = kid.Template(
168            file="template.kid", title=TITLE, user=USER, items=ITEMS
169        )
170        return template.serialize(output="xhtml")
171
172    if verbose:
173        print(render())
174    return render
175
176
177def run(engines, number=2000, verbose=False):
178    basepath = os.path.abspath(os.path.dirname(__file__))
179    for engine in engines:
180        dirname = os.path.join(basepath, engine)
181        if verbose:
182            print("%s:" % engine.capitalize())
183            print("--------------------------------------------------------")
184        else:
185            sys.stdout.write("%s:" % engine.capitalize())
186        t = timeit.Timer(
187            setup='from __main__ import %s; render = %s(r"%s", %s)'
188            % (engine, engine, dirname, verbose),
189            stmt="render()",
190        )
191
192        time = t.timeit(number=number) / number
193        if verbose:
194            print("--------------------------------------------------------")
195        print("%.2f ms" % (1000 * time))
196        if verbose:
197            print("--------------------------------------------------------")
198
199
200if __name__ == "__main__":
201    engines = [arg for arg in sys.argv[1:] if arg[0] != "-"]
202    if not engines:
203        engines = __all__
204
205    verbose = "-v" in sys.argv
206
207    if "-p" in sys.argv:
208        try:
209            import hotshot, hotshot.stats
210
211            prof = hotshot.Profile("template.prof")
212            benchtime = prof.runcall(run, engines, number=100, verbose=verbose)
213            stats = hotshot.stats.load("template.prof")
214        except ImportError:
215            import cProfile, pstats
216
217            stmt = "run(%r, number=%r, verbose=%r)" % (engines, 1000, verbose)
218            cProfile.runctx(stmt, globals(), {}, "template.prof")
219            stats = pstats.Stats("template.prof")
220        stats.strip_dirs()
221        stats.sort_stats("time", "calls")
222        stats.print_stats()
223    else:
224        run(engines, verbose=verbose)
225