|
@@ -479,8 +479,29 @@ class ExceptionReporter(object):
|
|
|
return lower_bound, pre_context, context_line, post_context
|
|
|
|
|
|
def get_traceback_frames(self):
|
|
|
+ def explicit_or_implicit_cause(exc_value):
|
|
|
+ explicit = getattr(exc_value, '__cause__', None)
|
|
|
+ implicit = getattr(exc_value, '__context__', None)
|
|
|
+ return explicit or implicit
|
|
|
+
|
|
|
+ # Get the exception and all its causes
|
|
|
+ exceptions = []
|
|
|
+ exc_value = self.exc_value
|
|
|
+ while exc_value:
|
|
|
+ exceptions.append(exc_value)
|
|
|
+ exc_value = explicit_or_implicit_cause(exc_value)
|
|
|
+
|
|
|
frames = []
|
|
|
- tb = self.tb
|
|
|
+ # No exceptions were supplied to ExceptionReporter
|
|
|
+ if not exceptions:
|
|
|
+ return frames
|
|
|
+
|
|
|
+ # In case there's just one exception (always in Python 2,
|
|
|
+ # sometimes in Python 3), take the traceback from self.tb (Python 2
|
|
|
+ # doesn't have a __traceback__ attribute on Exception)
|
|
|
+ exc_value = exceptions.pop()
|
|
|
+ tb = self.tb if not exceptions else exc_value.__traceback__
|
|
|
+
|
|
|
while tb is not None:
|
|
|
# Support for __traceback_hide__ which is used by a few libraries
|
|
|
# to hide internal frames.
|
|
@@ -497,6 +518,8 @@ class ExceptionReporter(object):
|
|
|
)
|
|
|
if pre_context_lineno is not None:
|
|
|
frames.append({
|
|
|
+ 'exc_cause': explicit_or_implicit_cause(exc_value),
|
|
|
+ 'exc_cause_explicit': getattr(exc_value, '__cause__', True),
|
|
|
'tb': tb,
|
|
|
'type': 'django' if module_name.startswith('django.') else 'user',
|
|
|
'filename': filename,
|
|
@@ -509,7 +532,14 @@ class ExceptionReporter(object):
|
|
|
'post_context': post_context,
|
|
|
'pre_context_lineno': pre_context_lineno + 1,
|
|
|
})
|
|
|
- tb = tb.tb_next
|
|
|
+
|
|
|
+ # If the traceback for current exception is consumed, try the
|
|
|
+ # other exception.
|
|
|
+ if not tb.tb_next and exceptions:
|
|
|
+ exc_value = exceptions.pop()
|
|
|
+ tb = exc_value.__traceback__
|
|
|
+ else:
|
|
|
+ tb = tb.tb_next
|
|
|
|
|
|
return frames
|
|
|
|
|
@@ -838,6 +868,15 @@ TECHNICAL_500_TEMPLATE = ("""
|
|
|
<div id="browserTraceback">
|
|
|
<ul class="traceback">
|
|
|
{% for frame in frames %}
|
|
|
+ {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
|
|
|
+ <li><h3>
|
|
|
+ {% if frame.exc_cause_explicit %}
|
|
|
+ The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:
|
|
|
+ {% else %}
|
|
|
+ During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:
|
|
|
+ {% endif %}
|
|
|
+ </h3></li>
|
|
|
+ {% endif %}{% endifchanged %}
|
|
|
<li class="frame {{ frame.type }}">
|
|
|
<code>{{ frame.filename|escape }}</code> in <code>{{ frame.function|escape }}</code>
|
|
|
|
|
@@ -1123,7 +1162,17 @@ In template {{ template_info.name }}, error at line {{ template_info.line }}
|
|
|
{{ source_line.0 }} : {{ source_line.1 }}
|
|
|
{% endifequal %}{% endfor %}{% endif %}{% if frames %}
|
|
|
Traceback:
|
|
|
-{% for frame in frames %}File "{{ frame.filename }}" in {{ frame.function }}
|
|
|
+{% for frame in frames %}
|
|
|
+{% ifchanged frame.exc_cause %}
|
|
|
+ {% if frame.exc_cause %}
|
|
|
+ {% if frame.exc_cause_explicit %}
|
|
|
+ The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:
|
|
|
+ {% else %}
|
|
|
+ During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:
|
|
|
+ {% endif %}
|
|
|
+ {% endif %}
|
|
|
+{% endifchanged %}
|
|
|
+File "{{ frame.filename }}" in {{ frame.function }}
|
|
|
{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line }}{% endif %}
|
|
|
{% endfor %}
|
|
|
{% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
|