| 1 | # -*- encoding: utf-8 -*- |
|---|
| 2 | # Template language benchmarks |
|---|
| 3 | # |
|---|
| 4 | # Objective: Test general templating features using a small template |
|---|
| 5 | |
|---|
| 6 | from cgi import escape |
|---|
| 7 | import os |
|---|
| 8 | from StringIO import StringIO |
|---|
| 9 | import sys |
|---|
| 10 | import timeit |
|---|
| 11 | |
|---|
| 12 | __all__ = ['clearsilver', 'mako', 'django', 'kid', 'genshi', 'genshi_text', |
|---|
| 13 | 'simpletal'] |
|---|
| 14 | |
|---|
| 15 | def genshi(dirname, verbose=False): |
|---|
| 16 | from genshi.template import TemplateLoader |
|---|
| 17 | loader = TemplateLoader([dirname], auto_reload=False) |
|---|
| 18 | template = loader.load('template.html') |
|---|
| 19 | def render(): |
|---|
| 20 | data = dict(title='Just a test', user='joe', |
|---|
| 21 | items=['Number %d' % num for num in range(1, 15)]) |
|---|
| 22 | return template.generate(**data).render('xhtml') |
|---|
| 23 | |
|---|
| 24 | if verbose: |
|---|
| 25 | print render() |
|---|
| 26 | return render |
|---|
| 27 | |
|---|
| 28 | def genshi_text(dirname, verbose=False): |
|---|
| 29 | from genshi.core import escape |
|---|
| 30 | from genshi.template import TemplateLoader, NewTextTemplate |
|---|
| 31 | loader = TemplateLoader([dirname], auto_reload=False) |
|---|
| 32 | template = loader.load('template.txt', cls=NewTextTemplate) |
|---|
| 33 | def render(): |
|---|
| 34 | data = dict(escape=escape, title='Just a test', user='joe', |
|---|
| 35 | items=['Number %d' % num for num in range(1, 15)]) |
|---|
| 36 | return template.generate(**data).render('text') |
|---|
| 37 | |
|---|
| 38 | if verbose: |
|---|
| 39 | print render() |
|---|
| 40 | return render |
|---|
| 41 | |
|---|
| 42 | def mako(dirname, verbose=False): |
|---|
| 43 | try: |
|---|
| 44 | from mako.lookup import TemplateLookup |
|---|
| 45 | except ImportError: |
|---|
| 46 | print>>sys.stderr, 'Mako not installed, skipping' |
|---|
| 47 | return lambda: None |
|---|
| 48 | lookup = TemplateLookup(directories=[dirname], filesystem_checks=False) |
|---|
| 49 | template = lookup.get_template('template.html') |
|---|
| 50 | def render(): |
|---|
| 51 | data = dict(title='Just a test', user='joe', |
|---|
| 52 | list_items=['Number %d' % num for num in range(1, 15)]) |
|---|
| 53 | return template.render(**data) |
|---|
| 54 | if verbose: |
|---|
| 55 | print render() |
|---|
| 56 | return render |
|---|
| 57 | |
|---|
| 58 | def cheetah(dirname, verbose=False): |
|---|
| 59 | # FIXME: infinite recursion somewhere... WTF? |
|---|
| 60 | try: |
|---|
| 61 | from Cheetah.Template import Template |
|---|
| 62 | except ImportError: |
|---|
| 63 | print>>sys.stderr, 'Cheetah not installed, skipping' |
|---|
| 64 | return lambda: None |
|---|
| 65 | class MyTemplate(Template): |
|---|
| 66 | def serverSidePath(self, path): return os.path.join(dirname, path) |
|---|
| 67 | filename = os.path.join(dirname, 'template.tmpl') |
|---|
| 68 | template = MyTemplate(file=filename) |
|---|
| 69 | |
|---|
| 70 | def render(): |
|---|
| 71 | template = MyTemplate(file=filename, |
|---|
| 72 | searchList=[{'title': 'Just a test', 'user': 'joe', |
|---|
| 73 | 'items': [u'Number %d' % num for num in range(1, 15)]}]) |
|---|
| 74 | return template.respond() |
|---|
| 75 | |
|---|
| 76 | if verbose: |
|---|
| 77 | print render() |
|---|
| 78 | return render |
|---|
| 79 | |
|---|
| 80 | def clearsilver(dirname, verbose=False): |
|---|
| 81 | try: |
|---|
| 82 | import neo_cgi |
|---|
| 83 | except ImportError: |
|---|
| 84 | print>>sys.stderr, 'ClearSilver not installed, skipping' |
|---|
| 85 | return lambda: None |
|---|
| 86 | neo_cgi.update() |
|---|
| 87 | import neo_util |
|---|
| 88 | import neo_cs |
|---|
| 89 | def render(): |
|---|
| 90 | hdf = neo_util.HDF() |
|---|
| 91 | hdf.setValue('hdf.loadpaths.0', dirname) |
|---|
| 92 | hdf.setValue('title', escape('Just a test')) |
|---|
| 93 | hdf.setValue('user', escape('joe')) |
|---|
| 94 | for num in range(1, 15): |
|---|
| 95 | hdf.setValue('items.%d' % (num - 1), escape('Number %d' % num)) |
|---|
| 96 | cs = neo_cs.CS(hdf) |
|---|
| 97 | cs.parseFile('template.cs') |
|---|
| 98 | return cs.render() |
|---|
| 99 | |
|---|
| 100 | if verbose: |
|---|
| 101 | print render() |
|---|
| 102 | return render |
|---|
| 103 | |
|---|
| 104 | def django(dirname, verbose=False): |
|---|
| 105 | try: |
|---|
| 106 | from django.conf import settings |
|---|
| 107 | settings.configure(TEMPLATE_DIRS=[os.path.join(dirname, 'templates')]) |
|---|
| 108 | except ImportError: |
|---|
| 109 | print>>sys.stderr, 'Django not installed, skipping' |
|---|
| 110 | return lambda: None |
|---|
| 111 | from django import template, templatetags |
|---|
| 112 | from django.template import loader |
|---|
| 113 | templatetags.__path__.append(os.path.join(dirname, 'templatetags')) |
|---|
| 114 | tmpl = loader.get_template('template.html') |
|---|
| 115 | |
|---|
| 116 | def render(): |
|---|
| 117 | data = {'title': 'Just a test', 'user': 'joe', |
|---|
| 118 | 'items': ['Number %d' % num for num in range(1, 15)]} |
|---|
| 119 | return tmpl.render(template.Context(data)) |
|---|
| 120 | |
|---|
| 121 | if verbose: |
|---|
| 122 | print render() |
|---|
| 123 | return render |
|---|
| 124 | |
|---|
| 125 | def kid(dirname, verbose=False): |
|---|
| 126 | try: |
|---|
| 127 | import kid |
|---|
| 128 | except ImportError: |
|---|
| 129 | print>>sys.stderr, "Kid not installed, skipping" |
|---|
| 130 | return lambda: None |
|---|
| 131 | kid.path = kid.TemplatePath([dirname]) |
|---|
| 132 | template = kid.load_template('template.kid').Template |
|---|
| 133 | def render(): |
|---|
| 134 | return template( |
|---|
| 135 | title='Just a test', user='joe', |
|---|
| 136 | items=['Number %d' % num for num in range(1, 15)] |
|---|
| 137 | ).serialize(output='xhtml') |
|---|
| 138 | |
|---|
| 139 | if verbose: |
|---|
| 140 | print render() |
|---|
| 141 | return render |
|---|
| 142 | |
|---|
| 143 | def simpletal(dirname, verbose=False): |
|---|
| 144 | try: |
|---|
| 145 | from simpletal import simpleTAL, simpleTALES |
|---|
| 146 | except ImportError: |
|---|
| 147 | print>>sys.stderr, "SimpleTAL not installed, skipping" |
|---|
| 148 | return lambda: None |
|---|
| 149 | fileobj = open(os.path.join(dirname, 'base.html')) |
|---|
| 150 | base = simpleTAL.compileHTMLTemplate(fileobj) |
|---|
| 151 | fileobj.close() |
|---|
| 152 | fileobj = open(os.path.join(dirname, 'template.html')) |
|---|
| 153 | template = simpleTAL.compileHTMLTemplate(fileobj) |
|---|
| 154 | fileobj.close() |
|---|
| 155 | def render(): |
|---|
| 156 | ctxt = simpleTALES.Context(allowPythonPath=1) |
|---|
| 157 | ctxt.addGlobal('base', base) |
|---|
| 158 | ctxt.addGlobal('title', 'Just a test') |
|---|
| 159 | ctxt.addGlobal('user', 'joe') |
|---|
| 160 | ctxt.addGlobal('items', ['Number %d' % num for num in range(1, 15)]) |
|---|
| 161 | buf = StringIO() |
|---|
| 162 | template.expand(ctxt, buf) |
|---|
| 163 | return buf.getvalue() |
|---|
| 164 | |
|---|
| 165 | if verbose: |
|---|
| 166 | print render() |
|---|
| 167 | return render |
|---|
| 168 | |
|---|
| 169 | def run(engines, number=2000, verbose=False): |
|---|
| 170 | basepath = os.path.abspath(os.path.dirname(__file__)) |
|---|
| 171 | for engine in engines: |
|---|
| 172 | dirname = os.path.join(basepath, engine) |
|---|
| 173 | if verbose: |
|---|
| 174 | print '%s:' % engine.capitalize() |
|---|
| 175 | print '--------------------------------------------------------' |
|---|
| 176 | else: |
|---|
| 177 | print '%s:' % engine.capitalize(), |
|---|
| 178 | t = timeit.Timer(setup='from __main__ import %s; render = %s(r"%s", %s)' |
|---|
| 179 | % (engine, engine, dirname, verbose), |
|---|
| 180 | stmt='render()') |
|---|
| 181 | time = t.timeit(number=number) / number |
|---|
| 182 | if verbose: |
|---|
| 183 | print '--------------------------------------------------------' |
|---|
| 184 | print '%.2f ms' % (1000 * time) |
|---|
| 185 | if verbose: |
|---|
| 186 | print '--------------------------------------------------------' |
|---|
| 187 | |
|---|
| 188 | |
|---|
| 189 | if __name__ == '__main__': |
|---|
| 190 | engines = [arg for arg in sys.argv[1:] if arg[0] != '-'] |
|---|
| 191 | if not engines: |
|---|
| 192 | engines = __all__ |
|---|
| 193 | |
|---|
| 194 | verbose = '-v' in sys.argv |
|---|
| 195 | |
|---|
| 196 | if '-p' in sys.argv: |
|---|
| 197 | import cProfile, pstats |
|---|
| 198 | prof = cProfile.Profile() |
|---|
| 199 | prof.run('run(%r, number=200, verbose=%r)' % (engines, verbose)) |
|---|
| 200 | stats = pstats.Stats(prof) |
|---|
| 201 | stats.strip_dirs() |
|---|
| 202 | stats.sort_stats('calls') |
|---|
| 203 | stats.print_stats(25) |
|---|
| 204 | if verbose: |
|---|
| 205 | stats.print_callees() |
|---|
| 206 | stats.print_callers() |
|---|
| 207 | else: |
|---|
| 208 | run(engines, verbose=verbose) |
|---|