From: Armin Ronacher Date: Thu, 1 May 2008 10:49:53 +0000 (+0200) Subject: small performance improvements X-Git-Tag: 2.0rc1~116 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=19cf9c20518b04886d95c9cfa7feb61df166c01d;p=jinja2.git small performance improvements --HG-- branch : trunk --- diff --git a/docs/api.rst b/docs/api.rst index 9562263..3ad17ac 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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: diff --git a/docs/templates.rst b/docs/templates.rst index 85171d6..7c0ed5c 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -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:: + + + .. function:: lipsum(n=5, html=True, min=20, max=100) Generates some lorem ipsum for the template. Per default five paragraphs diff --git a/examples/bench.py b/examples/bench.py index e27489c..8696be0 100644 --- a/examples/bench.py +++ b/examples/bench.py @@ -305,7 +305,7 @@ for test in 'jinja', 'mako', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah' stmt='bench()') sys.stdout.write(' >> %-20s' % 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 diff --git a/jinja2/compiler.py b/jinja2/compiler.py index b40c1d1..faa8b41 100644 --- a/jinja2/compiler.py +++ b/jinja2/compiler.py @@ -740,7 +740,8 @@ class CodeGenerator(NodeVisitor): 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 @@ class CodeGenerator(NodeVisitor): # 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". diff --git a/jinja2/environment.py b/jinja2/environment.py index 09a0ae2..8b959e5 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -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.lexer import Lexer 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 @@ class Environment(object): 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 @@ class Template(object): 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 @@ class Template(object): 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 @@ class Template(object): '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 @@ class Template(object): 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): diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 5233210..417fa70 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -16,8 +16,8 @@ from jinja2.exceptions import UndefinedError, TemplateRuntimeError # 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 @@ class TemplateContext(object): 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 @@ class Macro(object): 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 @@ class Macro(object): 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 @@ class Macro(object): (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):