small performance improvements
authorArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 May 2008 10:49:53 +0000 (12:49 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Thu, 1 May 2008 10:49:53 +0000 (12:49 +0200)
--HG--
branch : trunk

docs/api.rst
docs/templates.rst
examples/bench.py
jinja2/compiler.py
jinja2/environment.py
jinja2/runtime.py

index 95622639e62259456b8d406a45c4d9d70d8d43c8..3ad17acfeaef922d063554f9d47f280a381deaea 100644 (file)
@@ -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:
index 85171d69211dd19437ba41afaa96fad81e6d57b9..7c0ed5c96abc135d346d94df0de84c0628814b1b 100644 (file)
@@ -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
index e27489c31443e159963b7ead0d3f4c2216f48e56..8696be06c1c1980f24817d5c199f6bc4a8879ec1 100644 (file)
@@ -305,7 +305,7 @@ for test in 'jinja', 'mako', 'tenjin', 'spitfire', 'django', 'genshi', 'cheetah'
               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
index b40c1d16489448f774ffd238728645b3c450224b..faa8b4151853b0004d7b5ded3cfa6c8e7dcb4f95 100644 (file)
@@ -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".
index 09a0ae27ed15a1621a2d87600d855bb738d01501..8b959e5f6fc13a65a537fbb70dbbaaac87d00e18 100644 (file)
@@ -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):
index 5233210f5bf41cc409e87539929f5f3f1c3322eb..417fa707ec84ffada328baf2077d526002bea510 100644 (file)
@@ -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):