From 00d5d2149af1593d791f9cbd3a7c9df9eec85d4e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 13 Apr 2008 01:10:18 +0200 Subject: [PATCH] some more stuff for jinja2 --HG-- branch : trunk --- bench.py | 144 ++++++++++++++++++++++++++---- jinja2/compiler.py | 8 +- jinja2/environment.py | 1 - jinja2/nodes.py | 7 +- jinja2/optimizer.py | 26 ++++++ test_filter_and_linestatements.py | 3 + 6 files changed, 163 insertions(+), 26 deletions(-) diff --git a/bench.py b/bench.py index 1ead1ec..0e6b307 100644 --- a/bench.py +++ b/bench.py @@ -1,19 +1,127 @@ -from jinja import Environment as E1 -from jinja2 import Environment as E2 -from mako.template import Template as M - -t1, t2 = [e.from_string(""" - -""") for e in E1(), E2()] - -m = M(""" - +from django.conf import settings +settings.configure() +from django.template import Template as DjangoTemplate, Context as DjangoContext +from jinja2 import Environment as JinjaEnvironment +from mako.template import Template as MakoTemplate +from timeit import Timer + + +jinja_template = JinjaEnvironment( + line_statement_prefix='%', + variable_start_string="${", + variable_end_string="}" +).from_string("""\ + + + + ${page_title|e} + </head> + <body> + <div class="header"> + <h1>${page_title|e}</h1> + </div> + <ul class="navigation"> + % for href, caption in [ + ('index.html', 'Index'), + ('downloads.html', 'Downloads'), + ('products.html', 'Products') + ] + <li><a href="${href|e}">${caption|e}</a></li> + % endfor + </ul> + <div class="table"> + <table> + % for row in table + <tr> + % for cell in row + <td>${cell}</td> + % endfor + </tr> + % endfor + </table> + </div> + </body> +</html>\ +""") + +django_template = DjangoTemplate("""\ +<!doctype html> +<html> + <head> + <title>{{ page_title }} + </head> + <body> + <div class="header"> + <h1>{{ page_title }}</h1> + </div> + <ul class="navigation"> + {% for href, caption in navigation %} + <li><a href="{{ href }}">{{ caption }}</a></li> + {% endfor %} + </ul> + <div class="table"> + <table> + {% for row in table %} + <tr> + {% for cell in row %} + <td>{{ cell }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> + </div> + </body> +</html>\ +""") + +mako_template = MakoTemplate("""\ +<!doctype html> +<html> + <head> + <title>${page_title|h} + </head> + <body> + <div class="header"> + <h1>${page_title|h}</h1> + </div> + <ul class="navigation"> + % for href, caption in [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')]: + <li><a href="${href|h}">${caption|h}</a></li> + % endfor + </ul> + <div class="table"> + <table> + % for row in table: + <tr> + % for cell in row: + <td>${cell}</td> + % endfor + </tr> + % endfor + </table> + </div> + </body> +</html>\ """) + +context = { + 'page_title': 'mitsuhiko\'s benchmark', + '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)] +} + +def test_jinja(): + jinja_template.render(context) + +def test_django(): + c = DjangoContext(context) + c['navigation'] = [('index.html', 'Index'), ('downloads.html', 'Downloads'), ('products.html', 'Products')] + django_template.render(c) + +def test_mako(): + mako_template.render(**context) + + +for test in 'jinja', 'mako', 'django': + t = Timer(setup='from __main__ import test_%s as bench' % test, + stmt='bench()') + print '%-20s%.4fms' % (test, t.timeit(number=20) / 20) diff --git a/jinja2/compiler.py b/jinja2/compiler.py index e291f1d..e692184 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -564,9 +564,9 @@ class CodeGenerator(NodeVisitor): for arg in node.defaults: self.visit(arg) self.write(', ') - self.write('), %r, %r)' % ( - macro_frame.accesses_arguments, - macro_frame.accesses_caller + self.write('), %s, %s)' % ( + macro_frame.accesses_arguments and '1' or '0', + macro_frame.accesses_caller and '1' or '0' )) def visit_CallBlock(self, node, frame): @@ -581,7 +581,7 @@ class CodeGenerator(NodeVisitor): for arg in node.defaults: self.visit(arg) self.write(', ') - self.write('), %r, False)' % call_frame.accesses_arguments) + self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0')) if frame.buffer is None: self.writeline('yield ', node) else: diff --git a/jinja2/environment.py b/jinja2/environment.py index fb8101b..8458a23 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -107,7 +107,6 @@ class Environment(object): source = self.parse(source, filename) node = optimize(source, self) source = generate(node, self, filename) - print source if raw: return source if isinstance(filename, unicode): diff --git a/jinja2/nodes.py b/jinja2/nodes.py index 3b0ac3c..e1857d2 100644 --- a/jinja2/nodes.py +++ b/jinja2/nodes.py @@ -389,13 +389,14 @@ class Filter(Expr): """{{ foo|bar|baz }}""" fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs') - def as_const(self): - if self.node is None: + def as_const(self, obj=None): + if self.node is obj is None: raise Impossible() filter = self.environment.filters.get(self.name) if filter is None or getattr(filter, 'contextfilter', False): raise nodes.Impossible() - obj = self.node.as_const() + if obj is None: + obj = self.node.as_const() args = [x.as_const() for x in self.args] kwargs = dict(x.as_const() for x in self.kwargs) if self.dyn_args is not None: diff --git a/jinja2/optimizer.py b/jinja2/optimizer.py index bd97fa0..d2550f4 100644 --- a/jinja2/optimizer.py +++ b/jinja2/optimizer.py @@ -95,6 +95,32 @@ class Optimizer(NodeTransformer): finally: context.pop() + def visit_FilterBlock(self, node, context): + """Try to filter a block at compile time.""" + node = self.generic_visit(node, context) + context.push() + + # check if we can evaluate the wrapper body into a string + # at compile time + buffer = [] + for child in node.body: + if not isinstance(child, nodes.Output): + return node + for item in child.optimized_nodes(): + if isinstance(item, nodes.Node): + return node + buffer.append(item) + + # now check if we can evaluate the filter at compile time. + try: + data = node.filter.as_const(u''.join(buffer)) + except nodes.Impossible: + return node + + context.pop() + const = nodes.Const(data, lineno=node.lineno) + return nodes.Output([const], lineno=node.lineno) + def visit_For(self, node, context): """Loop unrolling for iterable constant values.""" try: diff --git a/test_filter_and_linestatements.py b/test_filter_and_linestatements.py index e5d94d0..c9e8f95 100644 --- a/test_filter_and_linestatements.py +++ b/test_filter_and_linestatements.py @@ -16,6 +16,9 @@ tmpl = env.from_string("""\ % endcall % filter escape <hello world> + % for item in [1, 2, 3] + - ${item} + % endfor % endfilter """) -- 2.26.2