Permalink
Browse files

small performance improvements

--HG--
branch : trunk
  • Loading branch information...
1 parent 7259c76 commit 19cf9c20518b04886d95c9cfa7feb61df166c01d @mitsuhiko mitsuhiko committed May 1, 2008
Showing with 59 additions and 44 deletions.
  1. +3 −3 docs/api.rst
  2. +13 −0 docs/templates.rst
  3. +1 −1 examples/bench.py
  4. +7 −6 jinja2/compiler.py
  5. +21 −22 jinja2/environment.py
  6. +14 −12 jinja2/runtime.py
View
@@ -123,13 +123,13 @@ disallows all operations beside testing if it's an undefined object.
The Context
-----------
-.. autoclass:: jinja2.runtime.TemplateContext
+.. autoclass:: jinja2.runtime.Context
:members: super, get, get_exported, get_all
.. attribute:: parent
A dict of read only, global variables the template looks up. These
- can either come from another :class:`TemplateContext`, from the
+ can either come from another :class:`Context`, from the
:attr:`Environment.globals` or :attr:`Template.globals`. It must not
be altered.
@@ -279,7 +279,7 @@ enabled::
return result
Context filters work the same just that the first argument is the current
-active :class:`TemplateContext` rather then the environment.
+active :class:`Context` rather then the environment.
.. _writing-tests:
View
@@ -797,6 +797,19 @@ The following functions are available in the global scope by default:
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
+ This is useful to repeat a template block multiple times for example
+ to fill a list. Imagine you have 7 users in the list but you want to
+ render three empty items to enforce a height with CSS::
+
+ <ul>
+ {% for user in users %}
+ <li>{{ user.username }}</li>
+ {% endfor %}
+ {% for number in range(10 - users|count) %}
+ <li class="empty"><span>...</span></li>
+ {% endfor %}
+ </ul>
+
.. function:: lipsum(n=5, html=True, min=20, max=100)
Generates some lorem ipsum for the template. Per default five paragraphs
View
@@ -305,7 +305,7 @@ def test_spitfire():
stmt='bench()')
sys.stdout.write(' >> %-20s<running>' % test)
sys.stdout.flush()
- sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=20) / 20))
+ sys.stdout.write('\r %-20s%.4f seconds\n' % (test, t.timeit(number=50) / 50))
sys.stdout.write('-' * 80 + '\n')
sys.stdout.write('''\
WARNING: The results of this benchmark are useless to compare the
View
@@ -740,7 +740,8 @@ def visit_FromImport(self, node, frame):
self.writeline('if l_%s is missing:' % alias)
self.indent()
self.writeline('l_%s = environment.undefined(%r %% '
- 'included_template.name)' %
+ 'included_template.name, '
+ 'name=included_template.name)' %
(alias, 'the template %r does not export '
'the requested name ' + repr(name)))
self.outdent()
@@ -770,11 +771,11 @@ def visit_For(self, node, frame):
# the expression pointing to the parent loop. We make the
# undefined a bit more debug friendly at the same time.
parent_loop = 'loop' in aliases and aliases['loop'] \
- or "environment.undefined(%r)" % "'loop' is undefined. " \
- 'the filter section of a loop as well as the ' \
- 'else block doesn\'t have access to the special ' \
- "'loop' variable of the current loop. Because " \
- 'there is no parent loop it\'s undefined.'
+ or "environment.undefined(%r, name='loop')" % "'loop' " \
+ 'is undefined. "the filter section of a loop as well ' \
+ 'as the else block doesn\'t have access to the ' \
+ "special 'loop' variable of the current loop. " \
+ "Because there is no parent loop it's undefined."
# if we have an extened loop and a node test, we filter in the
# "outer frame".
View
@@ -5,7 +5,7 @@
Provides a class that holds runtime and parsing time options.
- :copyright: 2007 by Armin Ronacher.
+ :copyright: 2008 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import sys
@@ -14,7 +14,7 @@
from jinja2.parser import Parser
from jinja2.optimizer import optimize
from jinja2.compiler import generate
-from jinja2.runtime import Undefined, TemplateContext, concat
+from jinja2.runtime import Undefined, Context, concat
from jinja2.debug import translate_exception
from jinja2.utils import import_string, LRUCache, Markup, missing
@@ -68,6 +68,7 @@ def _environment_sanity_check(environment):
environment.variable_start_string != \
environment.comment_start_string, 'block, variable and comment ' \
'start strings must be different'
+ return environment
class Environment(object):
@@ -148,6 +149,9 @@ class Environment(object):
#: True if the environment is just an overlay
overlay = False
+ #: the environment this environment is linked to if it is an overlay
+ linked_to = None
+
#: shared environments have this set to `True`. A shared environment
#: must not be modified
shared = False
@@ -250,8 +254,7 @@ def overlay(self, block_start_string=missing, block_end_string=missing,
if extensions is not missing:
rv.extensions.extend(load_extensions(extensions))
- _environment_sanity_check(rv)
- return rv
+ return _environment_sanity_check(rv)
@property
def lexer(self):
@@ -456,22 +459,16 @@ def render(self, *args, **kwargs):
This will return the rendered template as unicode string.
"""
try:
- return concat(self.generate(*args, **kwargs))
+ return concat(self._generate(*args, **kwargs))
except:
- # hide the `generate` frame
- exc_type, exc_value, tb = sys.exc_info()
- raise exc_type, exc_value, tb.tb_next
+ exc_type, exc_value, tb = translate_exception(sys.exc_info())
+ raise exc_type, exc_value, tb
def stream(self, *args, **kwargs):
"""Works exactly like :meth:`generate` but returns a
:class:`TemplateStream`.
"""
- try:
- return TemplateStream(self.generate(*args, **kwargs))
- except:
- # hide the `generate` frame
- exc_type, exc_value, tb = sys.exc_info()
- raise exc_type, exc_value, tb.tb_next
+ return TemplateStream(self.generate(*args, **kwargs))
def generate(self, *args, **kwargs):
"""For very large templates it can be useful to not render the whole
@@ -481,6 +478,14 @@ def generate(self, *args, **kwargs):
It accepts the same arguments as :meth:`render`.
"""
+ try:
+ for item in self._generate(*args, **kwargs):
+ yield item
+ except:
+ exc_type, exc_value, tb = translate_exception(sys.exc_info())
+ raise exc_type, exc_value, tb
+
+ def _generate(self, *args, **kwargs):
# assemble the context
context = dict(*args, **kwargs)
@@ -498,12 +503,7 @@ def generate(self, *args, **kwargs):
'will lead to unexpected results.' %
(plural, ', '.join(overrides), plural or ' a', plural))
- try:
- for event in self.root_render_func(self.new_context(context)):
- yield event
- except:
- exc_type, exc_value, tb = translate_exception(sys.exc_info())
- raise exc_type, exc_value, tb
+ return self.root_render_func(self.new_context(context))
def new_context(self, vars=None, shared=False):
"""Create a new template context for this template. The vars
@@ -519,8 +519,7 @@ def new_context(self, vars=None, shared=False):
parent = vars
else:
parent = dict(self.globals, **vars)
- return TemplateContext(self.environment, parent, self.name,
- self.blocks)
+ return Context(self.environment, parent, self.name, self.blocks)
@property
def module(self):
View
@@ -16,8 +16,8 @@
# these variables are exported to the template runtime
-__all__ = ['LoopContext', 'TemplateContext', 'TemplateReference', 'Macro',
- 'TemplateRuntimeError', 'Markup', 'missing', 'concat', 'escape',
+__all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
+ 'TemplateRuntimeError', 'missing', 'concat', 'escape',
'markup_join', 'unicode_join']
@@ -58,7 +58,7 @@ def unicode_join(*args):
return concat(imap(unicode, args))
-class TemplateContext(object):
+class Context(object):
"""The template context holds the variables of a template. It stores the
values passed to the template and also the names the template exports.
Creating instances is neither supported nor useful as it's created
@@ -105,7 +105,8 @@ def super(self, name, current):
raise IndexError()
except LookupError:
return self.environment.undefined('there is no parent block '
- 'called %r.' % name)
+ 'called %r.' % name,
+ name='super')
wrap = self.environment.autoescape and Markup or (lambda x: x)
render = lambda: wrap(concat(blocks[pos](self)))
render.__name__ = render.name = name
@@ -239,22 +240,19 @@ def __init__(self, environment, func, name, arguments, defaults,
self.caller = caller
def __call__(self, *args, **kwargs):
- if not self.catch_varargs and len(args) > self._argument_count:
- raise TypeError('macro %r takes not more than %d argument(s)' %
- (self.name, len(self.arguments)))
arguments = []
for idx, name in enumerate(self.arguments):
try:
value = args[idx]
- except IndexError:
+ except:
try:
value = kwargs.pop(name)
- except KeyError:
+ except:
try:
value = self.defaults[idx - self._argument_count]
- except IndexError:
+ except:
value = self._environment.undefined(
- 'parameter %r was not provided' % name)
+ 'parameter %r was not provided' % name, name=name)
arguments.append(value)
# it's important that the order of these arguments does not change
@@ -263,7 +261,8 @@ def __call__(self, *args, **kwargs):
if self.caller:
caller = kwargs.pop('caller', None)
if caller is None:
- caller = self._environment.undefined('No caller defined')
+ caller = self._environment.undefined('No caller defined',
+ name='caller')
arguments.append(caller)
if self.catch_kwargs:
arguments.append(kwargs)
@@ -272,6 +271,9 @@ def __call__(self, *args, **kwargs):
(self.name, iter(kwargs).next()))
if self.catch_varargs:
arguments.append(args[self._argument_count:])
+ elif len(args) > self._argument_count:
+ raise TypeError('macro %r takes not more than %d argument(s)' %
+ (self.name, len(self.arguments)))
return self._func(*arguments)
def __repr__(self):

0 comments on commit 19cf9c2

Please sign in to comment.