Undefineds now support attribute errors for special attributes. This fixes #6
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 19 Nov 2010 12:51:38 +0000 (13:51 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 19 Nov 2010 12:51:38 +0000 (13:51 +0100)
CHANGES
jinja2/runtime.py
jinja2/testsuite/api.py
jinja2/testsuite/security.py

diff --git a/CHANGES b/CHANGES
index 182f0b043c8536aaa38fb861c53661758c8cc330..17a805bb1515210283094a62a371e95db07b45c0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,10 @@ Version 2.6
 -----------
 (codename to be selected, release date to be announced)
 
+- internal attributes now raise an internal attribute error now instead
+  of returning an undefined.  This fixes problems when passing undefined
+  objects to Python semantics expecting APIs.
+
 Version 2.5.5
 -------------
 (re-release of 2.5.4 with built documentation removed for filesize.
index 6fea3aa4fb5feda4207e464a24526cf769a0b281..9bdeb011f918b625a78fbb400afa5ccfa6cac51d 100644 (file)
@@ -458,11 +458,17 @@ class Undefined(object):
             hint = self._undefined_hint
         raise self._undefined_exception(hint)
 
+    @internalcode
+    def __getattr__(self, name):
+        if name[:2] == '__':
+            raise AttributeError(name)
+        return self._fail_with_undefined_error()
+
     __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
     __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
     __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
-    __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
-    __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
+    __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \
+    __float__ = __complex__ = __pow__ = __rpow__ = \
         _fail_with_undefined_error
 
     def __str__(self):
index 7463c7f3f298a1448c8224016faade813b0f728b..7e11b83c743e9d6ff2a2fec9326f04830a9bd781 100644 (file)
@@ -180,6 +180,14 @@ class UndefinedTestCase(JinjaTestCase):
         t = Template('A{{ test().missingattribute }}B')
         self.assert_raises(UndefinedError, t.render, test=test)
 
+    def test_undefined_and_special_attributes(self):
+        try:
+            Undefined('Foo').__dict__
+        except AttributeError:
+            pass
+        else:
+            assert False, "Expected actual attribute error"
+
     def test_default_undefined(self):
         env = Environment(undefined=Undefined)
         self.assert_equal(env.from_string('{{ missing }}').render(), u'')
index b2b4cf13e1fea49594389a71104f47d732c3d8ac..a95705f6e8233f94c23f8e747a25ccbc17ff7e8b 100644 (file)
@@ -56,6 +56,7 @@ class SandboxTestCase(JinjaTestCase):
         self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23')
         self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '')
         self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '')
+        # security error comes from __class__ already.
         self.assert_raises(SecurityError, env.from_string(
             "{{ foo.__class__.__subclasses__() }}").render, foo=42)
 
@@ -108,7 +109,6 @@ class SandboxTestCase(JinjaTestCase):
         assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
         assert Markup("&lt;test&gt;").unescape() == "<test>"
 
-
     def test_template_data(self):
         env = Environment(autoescape=True)
         t = env.from_string('{% macro say_hello(name) %}'
@@ -121,11 +121,10 @@ class SandboxTestCase(JinjaTestCase):
         assert t.module.say_hello('<blink>foo</blink>') == escaped_out
         assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
 
-
     def test_attr_filter(self):
         env = SandboxedEnvironment()
-        tmpl = env.from_string('{{ 42|attr("__class__")|attr("__subclasses__")() }}')
-        self.assert_raises(SecurityError, tmpl.render)
+        tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
+        self.assert_raises(SecurityError, tmpl.render, cls=int)
 
 
 def suite():