Calls to functions in templates are now intercepted for StopIteration.
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 5 Jun 2010 12:32:06 +0000 (14:32 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 5 Jun 2010 12:32:06 +0000 (14:32 +0200)
Improved performance of macro call slightly.

--HG--
branch : trunk

jinja2/runtime.py
jinja2/testsuite/api.py

index 43ffda80ae3360c6f7df8575ba20999e0cf03343..6fea3aa4fb5feda4207e464a24526cf769a0b281 100644 (file)
@@ -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.
index 42054f51706145b7a5582e26305927981419a6fc..7463c7f3f298a1448c8224016faade813b0f728b 100644 (file)
@@ -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'')