#12554 closed (fixed)
silent_variable_failure not suppressing exception during dict lookup
| Reported by: | Owned by: | nobody | |
|---|---|---|---|
| Component: | Template system | Version: | dev |
| Severity: | Keywords: | ||
| Cc: | Triage Stage: | Accepted | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | yes |
| Easy pickings: | no | UI/UX: | no |
Description
Example:
from django.template import Context, Template
class E(Exception): silent_variable_failure = True
class A(object):
def __getitem__(self, key):
raise E
t = Template("{{ a.b }}")
t.render(Context({"a": A()}))
According to Django docs, the exception should be suppresed and an empty string should be returned. However, instead, Django raises the exception:
---------------------------------------------------------------------------
TemplateSyntaxError Traceback (most recent call last)
/home/user/user/<ipython console> in <module>()
/usr/lib/python2.5/site-packages/django/template/__init__.pyc in render(self, context)
176 def render(self, context):
177 "Display stage -- can be called many times"
--> 178 return self.nodelist.render(context)
179
180 def compile_string(template_string, origin):
/usr/lib/python2.5/site-packages/django/template/__init__.pyc in render(self, context)
777 for node in self:
778 if isinstance(node, Node):
--> 779 bits.append(self.render_node(node, context))
780 else:
781 bits.append(node)
/usr/lib/python2.5/site-packages/django/template/debug.py in render_node(self, node, context)
79 wrapped.source = node.source
80 wrapped.exc_info = exc_info()
---> 81 raise wrapped
82 return result
83
TemplateSyntaxError: Caught an exception while rendering:
Original Traceback (most recent call last):
File "/usr/lib/python2.5/site-packages/django/template/debug.py", line 71, in render_node
result = node.render(context)
File "/usr/lib/python2.5/site-packages/django/template/debug.py", line 87, in render
output = force_unicode(self.filter_expression.resolve(context))
File "/usr/lib/python2.5/site-packages/django/template/__init__.py", line 546, in resolve
obj = self.var.resolve(context)
File "/usr/lib/python2.5/site-packages/django/template/__init__.py", line 687, in resolve
value = self._resolve_lookup(context)
File "/usr/lib/python2.5/site-packages/django/template/__init__.py", line 713, in _resolve_lookup
current = current[bit]
File "<ipython console>", line 4, in __getitem__
E
Proposed patch:
Index: django/template/__init__.py
===================================================================
--- django/template/__init__.py (revision 11929)
+++ django/template/__init__.py (working copy)
@@ -745,11 +745,11 @@
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
- else:
- raise
+ except Exception, e:
+ if getattr(e, 'silent_variable_failure', False):
+ current = settings.TEMPLATE_STRING_IF_INVALID
+ else:
+ raise
return current
Attachments (3)
Change History (16)
comment:1 by , 16 years ago
| milestone: | → 1.2 |
|---|---|
| Patch needs improvement: | set |
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 16 years ago
comment:3 by , 16 years ago
I realized I wasn't working from the trunk. Here is a real proposed patch:
===================================================================
--- __init__.py (revision 12610)
+++ __init__.py (working copy)
@@ -583,7 +583,24 @@
if not lookup:
arg_vals.append(mark_safe(arg))
else:
- arg_vals.append(arg.resolve(context))
+ #arg_vals.append(arg.resolve(context))
+ try:
+ arg_val = arg.resolve(context)
+ except VariableDoesNotExist:
+ if ignore_failures:
+ arg_val = None
+ else:
+ if settings.TEMPLATE_STRING_IF_INVALID:
+ global invalid_var_format_string
+ if invalid_var_format_string is None:
+ invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
+ if invalid_var_format_string:
+ arg_val= settings.TEMPLATE_STRING_IF_INVALID % self.var
+ else:
+ arg_val= settings.TEMPLATE_STRING_IF_INVALID
+ else:
+ arg_val = settings.TEMPLATE_STRING_IF_INVALID
+ arg_vals.append(arg_val)
if getattr(func, 'needs_autoescape', False):
Here is a test case:
>>> from django.template import Context, Template
>>> t = Template("{{'test'|default:notreal}}")
>>> t.render(Context())
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/opt/local/OnQueue2/OQTools/pythonlib/trunk/django/template/__init__.py", line 184, in render
return self._render(context)
File "/opt/local/OnQueue2/OQTools/pythonlib/trunk/django/template/__init__.py", line 178, in _render
return self.nodelist.render(context)
File "/opt/local/OnQueue2/OQTools/pythonlib/trunk/django/template/__init__.py", line 799, in render
bits.append(self.render_node(node, context))
File "/opt/local/OnQueue2/OQTools/pythonlib/trunk/django/template/debug.py", line 82, in render_node
raise wrapped
TemplateSyntaxError: Caught an exception while rendering: Failed lookup for key [notreal] in u'[{}]'
comment:4 by , 16 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:5 by , 16 years ago
| Owner: | changed from to |
|---|---|
| Status: | assigned → new |
comment:6 by , 16 years ago
This looks to me like two different problems: the problem in the original description regarding an exception with silent_variable_failure=True not being silenced, and a different problem with non-existent variables passed to filters raising exceptions. I'd like to narrow the focus of this ticket to the first problem, which seems clearly wrong. (I'm less sure of what the right behavior is supposed to be for the 2nd situation.)
comment:7 by , 16 years ago
Since we're not sure what should happen in the template filter scenario, I opened #13167.
comment:8 by , 16 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
comment:9 by , 16 years ago
comment:10 by , 16 years ago
| Resolution: | fixed |
|---|---|
| Status: | closed → reopened |
When I do this:
from django.template import Context, Template
from myapp.models import MyModel
MyModel(models.Model):
some = model.OneToOneField(AnotherModel)
obj = MyModel.objects.get(id=1)
t = Template("{{ obj.some }}")
t.render(Context({"obj": obj}))
Django raises the exception:
---------------------------------------------------------------------------
TemplateSyntaxError Traceback (most recent call last)
/home/alejandro/inmegen/<ipython console> in <module>()
/usr/lib/python2.6/site-packages/django/template/__init__.pyc in render(self, context)
169 context.render_context.push()
170 try:
--> 171 return self._render(context)
172 finally:
173 context.render_context.pop()
/usr/lib/python2.6/site-packages/django/template/__init__.pyc in _render(self, context)
163
164 def _render(self, context):
--> 165 return self.nodelist.render(context)
166
167 def render(self, context):
/usr/lib/python2.6/site-packages/django/template/__init__.pyc in render(self, context)
795 for node in self:
796 if isinstance(node, Node):
--> 797 bits.append(self.render_node(node, context))
798 else:
799 bits.append(node)
TemplateSyntaxError: Caught DoesNotExist while rendering: AnotherModel matching query does not exist.
Proposed patch:
Index: django/template/__init__.py
===================================================================
--- django/template/__init__.py (revision 12823)
+++ django/template/__init__.py (working copy)
@@ -745,6 +745,11 @@
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
+ else:
+ raise
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = settings.TEMPLATE_STRING_IF_INVALID
comment:11 by , 16 years ago
| Resolution: | → fixed |
|---|---|
| Status: | reopened → closed |
The same error occurs when a variable that doesn't exist is referenced as the argument of a filter
for example:
{% if use_styleId|default_if_none:my_styleId %}The stack trace shows the problem to be the naked call to resolve at around line 576:
My quick fix was to duplicate much of the exception logic used in the primary case just above:
try: arg_val = arg.resolve(context) except VariableDoesNotExist: if ignore_failures: arg_val = None else: if settings.TEMPLATE_STRING_IF_INVALID: global invalid_var_format_string if invalid_var_format_string is None: invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID if invalid_var_format_string: arg_val= settings.TEMPLATE_STRING_IF_INVALID % self.var else: arg_val= settings.TEMPLATE_STRING_IF_INVALID else: arg_val = settings.TEMPLATE_STRING_IF_INVALID arg_vals.append(arg_val)However, it seems to me that Variable.resolve() should be taking care of the settings.TEMPLATE_STRING_IF_INVALID semantic, not FilterExpression.
The patch suggested doesn't seem to do anything since VariableDoesNotExist is not derived from ObjectDoesNotExist. I did not use it.