Improved attribute and item lookup by allowing template designers to express the priority.  foo.bar checks foo.bar first and then foo['bar'] and the other way round.

--HG--
branch : trunk
diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py
index 7135ff0..20de369 100644
--- a/jinja2/sandbox.py
+++ b/jinja2/sandbox.py
@@ -183,7 +183,7 @@
         return not (getattr(obj, 'unsafe_callable', False) or \
                     getattr(obj, 'alters_data', False))
 
-    def subscribe(self, obj, argument):
+    def getitem(self, obj, argument):
         """Subscribe an object from sandboxed code."""
         try:
             return obj[argument]
@@ -201,13 +201,34 @@
                     else:
                         if self.is_safe_attribute(obj, argument, value):
                             return value
-                        return self.undefined('access to attribute %r of %r '
-                                              'object is unsafe.' % (
-                            argument,
-                            obj.__class__.__name__
-                        ), name=argument, obj=obj, exc=SecurityError)
+                        return self.unsafe_undefined(obj, argument)
         return self.undefined(obj=obj, name=argument)
 
+    def getattr(self, obj, attribute):
+        """Subscribe an object from sandboxed code and prefer the
+        attribute.  The attribute passed *must* be a bytestring.
+        """
+        try:
+            value = getattr(obj, attribute)
+        except AttributeError:
+            try:
+                return obj[argument]
+            except (TypeError, LookupError):
+                pass
+        else:
+            if self.is_safe_attribute(obj, attribute, value):
+                return value
+            return self.unsafe_undefined(obj, attribute)
+        return self.undefined(obj=obj, name=argument)
+
+    def unsafe_undefined(self, obj, attribute):
+        """Return an undefined object for unsafe attributes."""
+        return self.undefined('access to attribute %r of %r '
+                              'object is unsafe.' % (
+            attribute,
+            obj.__class__.__name__
+        ), name=attribute, obj=obj, exc=SecurityError)
+
     def call(__self, __context, __obj, *args, **kwargs):
         """Call an object from sandboxed code."""
         # the double prefixes are to avoid double keyword argument