From 3351a93bf752825a15acdbcc37719cbee63718ef Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 5 Jun 2010 14:32:06 +0200 Subject: [PATCH] Calls to functions in templates are now intercepted for StopIteration. Improved performance of macro call slightly. --HG-- branch : trunk --- jinja2/runtime.py | 31 ++++++++++++++++++++----------- jinja2/testsuite/api.py | 8 ++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/jinja2/runtime.py b/jinja2/runtime.py index 43ffda8..6fea3aa 100644 --- a/jinja2/runtime.py +++ b/jinja2/runtime.py @@ -179,7 +179,12 @@ class Context(object): args = (__self.eval_ctx,) + args elif getattr(__obj, 'environmentfunction', 0): args = (__self.environment,) + args - return __obj(*args, **kwargs) + try: + return __obj(*args, **kwargs) + except StopIteration: + return __self.environment.undefined('value was undefined because ' + 'a callable raised a ' + 'StopIteration exception') def derived(self, locals=None): """Internal helper function to create a derived context.""" @@ -345,7 +350,7 @@ class LoopContextIterator(object): class Macro(object): - """Wraps a macro.""" + """Wraps a macro function.""" def __init__(self, environment, func, name, arguments, defaults, catch_kwargs, catch_varargs, caller): @@ -361,20 +366,24 @@ class Macro(object): @internalcode def __call__(self, *args, **kwargs): - arguments = [] - for idx, name in enumerate(self.arguments): - try: - value = args[idx] - except: + # try to consume the positional arguments + arguments = list(args[:self._argument_count]) + off = len(arguments) + + # if the number of arguments consumed is not the number of + # arguments expected we start filling in keyword arguments + # and defaults. + if off != self._argument_count: + for idx, name in enumerate(self.arguments[len(arguments):]): try: value = kwargs.pop(name) - except: + except KeyError: try: - value = self.defaults[idx - self._argument_count] - except: + value = self.defaults[idx - self._argument_count + off] + except IndexError: value = self._environment.undefined( 'parameter %r was not provided' % name, name=name) - arguments.append(value) + arguments.append(value) # it's important that the order of these arguments does not change # if not also changed in the compiler's `function_scoping` method. diff --git a/jinja2/testsuite/api.py b/jinja2/testsuite/api.py index 42054f5..7463c7f 100644 --- a/jinja2/testsuite/api.py +++ b/jinja2/testsuite/api.py @@ -172,6 +172,14 @@ class StreamingTestCase(JinjaTestCase): class UndefinedTestCase(JinjaTestCase): + def test_stopiteration_is_undefined(self): + def test(): + raise StopIteration() + t = Template('A{{ test() }}B') + assert t.render(test=test) == 'AB' + t = Template('A{{ test().missingattribute }}B') + self.assert_raises(UndefinedError, t.render, test=test) + def test_default_undefined(self): env = Environment(undefined=Undefined) self.assert_equal(env.from_string('{{ missing }}').render(), u'') -- 2.26.2