Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #7153 -- _resolve_lookup now does a better job of resolving cal…

…lables and correctly catches all silent_variable_exceptions

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14992 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit df6ad35c56c270ea04865e43a83b1ff0f35a71f4 1 parent 6fc7d82
@SmileyChris SmileyChris authored
Showing with 61 additions and 38 deletions.
  1. +29 −38 django/template/base.py
  2. +32 −0 tests/regressiontests/templates/tests.py
View
67 django/template/base.py
@@ -674,46 +674,37 @@ def _resolve_lookup(self, context):
instead.
"""
current = context
- for bit in self.lookups:
- try: # dictionary lookup
- current = current[bit]
- except (TypeError, AttributeError, KeyError):
- try: # attribute lookup
- current = getattr(current, bit)
- if callable(current):
- if getattr(current, 'alters_data', False):
- current = settings.TEMPLATE_STRING_IF_INVALID
- else:
- try: # method call (assuming no args required)
- current = current()
- except TypeError: # arguments *were* required
- # GOTCHA: This will also catch any TypeError
- # raised in the function itself.
- 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
- 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):
+ try: # catch-all for silent variable failures
+ for bit in self.lookups:
+ try: # dictionary lookup
+ current = current[bit]
+ except (TypeError, AttributeError, KeyError):
+ try: # attribute lookup
+ current = getattr(current, bit)
+ 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
+ if callable(current):
+ if getattr(current, 'alters_data', False):
current = settings.TEMPLATE_STRING_IF_INVALID
else:
- raise
- except Exception, e:
- if getattr(e, 'silent_variable_failure', False):
- current = settings.TEMPLATE_STRING_IF_INVALID
- else:
- raise
+ try: # method call (assuming no args required)
+ current = current()
+ except TypeError: # arguments *were* required
+ # GOTCHA: This will also catch any TypeError
+ # raised in the function itself.
+ 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
View
32 tests/regressiontests/templates/tests.py
@@ -91,6 +91,21 @@ def method3(self):
def method4(self):
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:
def method(self):
return "OtherClass.method"
@@ -529,6 +544,12 @@ def get_template_tests(self):
'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"),
'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-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
@@ -616,6 +637,17 @@ def get_template_tests(self):
#filters should accept empty string constants
'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-syntax01': ("{# this is hidden #}hello", {}, "hello"),
'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
Please sign in to comment.
Something went wrong with that request. Please try again.