0
0
mirror of https://github.com/django/django.git synced 2024-12-01 15:42:04 +01:00

Fixed #7153 -- _resolve_lookup now does a better job of resolving callables and correctly catches all silent_variable_exceptions

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14992 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Chris Beaven 2010-12-19 23:47:24 +00:00
parent 6fc7d829dc
commit df6ad35c56
2 changed files with 61 additions and 38 deletions

View File

@ -674,46 +674,37 @@ class Variable(object):
instead. instead.
""" """
current = context current = context
for bit in self.lookups: try: # catch-all for silent variable failures
try: # dictionary lookup for bit in self.lookups:
current = current[bit] try: # dictionary lookup
except (TypeError, AttributeError, KeyError): current = current[bit]
try: # attribute lookup except (TypeError, AttributeError, KeyError):
current = getattr(current, bit) try: # attribute lookup
if callable(current): current = getattr(current, bit)
if getattr(current, 'alters_data', False): except (TypeError, AttributeError):
current = settings.TEMPLATE_STRING_IF_INVALID try: # list-index lookup
else: current = current[int(bit)]
try: # method call (assuming no args required) except (IndexError, # list index out of range
current = current() ValueError, # invalid literal for int()
except TypeError: # arguments *were* required KeyError, # current is a dict without `int(bit)` key
# GOTCHA: This will also catch any TypeError TypeError, # unsubscriptable object
# raised in the function itself. ):
current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
except Exception, e: if callable(current):
if getattr(e, 'silent_variable_failure', False): if getattr(current, 'alters_data', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
except (TypeError, AttributeError):
try: # list-index lookup
current = current[int(bit)]
except (IndexError, # list index out of range
ValueError, # invalid literal for int()
KeyError, # current is a dict without `int(bit)` key
TypeError, # unsubscriptable object
):
raise VariableDoesNotExist("Failed lookup for key [%s] in %r", (bit, current)) # missing attribute
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = settings.TEMPLATE_STRING_IF_INVALID current = settings.TEMPLATE_STRING_IF_INVALID
else: else:
raise try: # method call (assuming no args required)
except Exception, e: current = current()
if getattr(e, 'silent_variable_failure', False): except TypeError: # arguments *were* required
current = settings.TEMPLATE_STRING_IF_INVALID # GOTCHA: This will also catch any TypeError
else: # raised in the function itself.
raise current = settings.TEMPLATE_STRING_IF_INVALID # invalid method call
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
raise
return current return current

View File

@ -91,6 +91,21 @@ class SomeClass:
def method4(self): def method4(self):
raise SomeOtherException raise SomeOtherException
def __getitem__(self, key):
if key == 'silent_fail_key':
raise SomeException
elif key == 'noisy_fail_key':
raise SomeOtherException
raise KeyError
def silent_fail_attribute(self):
raise SomeException
silent_fail_attribute = property(silent_fail_attribute)
def noisy_fail_attribute(self):
raise SomeOtherException
noisy_fail_attribute = property(noisy_fail_attribute)
class OtherClass: class OtherClass:
def method(self): def method(self):
return "OtherClass.method" return "OtherClass.method"
@ -529,6 +544,12 @@ class Templates(unittest.TestCase):
'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"), 'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"),
'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"), 'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"),
# Call methods in the top level of the context
'basic-syntax37': ('{{ callable }}', {"callable": lambda: "foo bar"}, "foo bar"),
# Call methods returned from dictionary lookups
'basic-syntax38': ('{{ var.callable }}', {"var": {"callable": lambda: "foo bar"}}, "foo bar"),
# List-index syntax allows a template to access a certain item of a subscriptable object. # List-index syntax allows a template to access a certain item of a subscriptable object.
'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"), 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
@ -616,6 +637,17 @@ class Templates(unittest.TestCase):
#filters should accept empty string constants #filters should accept empty string constants
'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""), 'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),
# Fail silently for non-callable attribute and dict lookups which
# raise an exception with a "silent_variable_failure" attribute
'filter-syntax21': (r'1{{ var.silent_fail_key }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
'filter-syntax22': (r'1{{ var.silent_fail_attribute }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
# In attribute and dict lookups that raise an unexpected exception
# without a "silent_variable_attribute" set to True, the exception
# propagates
'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, SomeOtherException),
'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, SomeOtherException),
### COMMENT SYNTAX ######################################################## ### COMMENT SYNTAX ########################################################
'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"), 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),