@@ -479,8 +479,29 @@ def _get_lines_from_file(self, filename, lineno, context_lines, loader=None, mod
479479 return lower_bound , pre_context , context_line , post_context
480480
481481 def get_traceback_frames (self ):
482+ def explicit_or_implicit_cause (exc_value ):
483+ explicit = getattr (exc_value , '__cause__' , None )
484+ implicit = getattr (exc_value , '__context__' , None )
485+ return explicit or implicit
486+
487+ # Get the exception and all its causes
488+ exceptions = []
489+ exc_value = self .exc_value
490+ while exc_value :
491+ exceptions .append (exc_value )
492+ exc_value = explicit_or_implicit_cause (exc_value )
493+
482494 frames = []
483- tb = self .tb
495+ # No exceptions were supplied to ExceptionReporter
496+ if not exceptions :
497+ return frames
498+
499+ # In case there's just one exception (always in Python 2,
500+ # sometimes in Python 3), take the traceback from self.tb (Python 2
501+ # doesn't have a __traceback__ attribute on Exception)
502+ exc_value = exceptions .pop ()
503+ tb = self .tb if not exceptions else exc_value .__traceback__
504+
484505 while tb is not None :
485506 # Support for __traceback_hide__ which is used by a few libraries
486507 # to hide internal frames.
@@ -497,6 +518,8 @@ def get_traceback_frames(self):
497518 )
498519 if pre_context_lineno is not None :
499520 frames .append ({
521+ 'exc_cause' : explicit_or_implicit_cause (exc_value ),
522+ 'exc_cause_explicit' : getattr (exc_value , '__cause__' , True ),
500523 'tb' : tb ,
501524 'type' : 'django' if module_name .startswith ('django.' ) else 'user' ,
502525 'filename' : filename ,
@@ -509,7 +532,14 @@ def get_traceback_frames(self):
509532 'post_context' : post_context ,
510533 'pre_context_lineno' : pre_context_lineno + 1 ,
511534 })
512- tb = tb .tb_next
535+
536+ # If the traceback for current exception is consumed, try the
537+ # other exception.
538+ if not tb .tb_next and exceptions :
539+ exc_value = exceptions .pop ()
540+ tb = exc_value .__traceback__
541+ else :
542+ tb = tb .tb_next
513543
514544 return frames
515545
@@ -838,6 +868,15 @@ def default_urlconf(request):
838868 <div id="browserTraceback">
839869 <ul class="traceback">
840870 {% for frame in frames %}
871+ {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}
872+ <li><h3>
873+ {% if frame.exc_cause_explicit %}
874+ The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:
875+ {% else %}
876+ During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:
877+ {% endif %}
878+ </h3></li>
879+ {% endif %}{% endifchanged %}
841880 <li class="frame {{ frame.type }}">
842881 <code>{{ frame.filename|escape }}</code> in <code>{{ frame.function|escape }}</code>
843882
@@ -1123,7 +1162,17 @@ def default_urlconf(request):
11231162 {{ source_line.0 }} : {{ source_line.1 }}
11241163 {% endifequal %}{% endfor %}{% endif %}{% if frames %}
11251164Traceback:
1126- {% for frame in frames %}File "{{ frame.filename }}" in {{ frame.function }}
1165+ {% for frame in frames %}
1166+ {% ifchanged frame.exc_cause %}
1167+ {% if frame.exc_cause %}
1168+ {% if frame.exc_cause_explicit %}
1169+ The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:
1170+ {% else %}
1171+ During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:
1172+ {% endif %}
1173+ {% endif %}
1174+ {% endifchanged %}
1175+ File "{{ frame.filename }}" in {{ frame.function }}
11271176{% if frame.context_line %} {{ frame.lineno }}. {{ frame.context_line }}{% endif %}
11281177{% endfor %}
11291178{% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}
0 commit comments