diff --git a/django/views/debug.py b/django/views/debug.py index 47c91cc943..e2f9ffe3e2 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -439,7 +439,13 @@ class ExceptionReporter: def get_exception_traceback_frames(self, exc_value, tb): exc_cause = self._get_explicit_or_implicit_cause(exc_value) exc_cause_explicit = getattr(exc_value, '__cause__', True) - + if tb is None: + yield { + 'exc_cause': exc_cause, + 'exc_cause_explicit': exc_cause_explicit, + 'tb': None, + 'type': 'user', + } while tb is not None: # Support for __traceback_hide__ which is used by a few libraries # to hide internal frames. diff --git a/django/views/templates/technical_500.html b/django/views/templates/technical_500.html index f0cadf03bd..0ed70d50ca 100644 --- a/django/views/templates/technical_500.html +++ b/django/views/templates/technical_500.html @@ -225,7 +225,11 @@ {% endif %}{% endifchanged %}
  • - {{ frame.filename }}, line {{ frame.lineno }}, in {{ frame.function }} + {% if not frame.tb %} + {% if forloop.first %}None{% else %}Traceback: None{% endif %} + {% else %} + {{ frame.filename }}, line {{ frame.lineno }}, in {{ frame.function }} + {% endif %} {% if frame.context_line %}
    @@ -319,8 +323,8 @@ Traceback (most recent call last):{% for frame in frames %} The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception: {% else %} During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred: -{% endif %}{% endif %}{% endifchanged %} File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} -{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %}{% endfor %} +{% endif %}{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} +{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %}{% else %}{% if forloop.first %}None{% else %}Traceback: None{% endif %}{% endif %}{% endfor %} Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %} Exception Value: {{ exception_value|force_escape }} diff --git a/django/views/templates/technical_500.txt b/django/views/templates/technical_500.txt index 71a6e150d8..551413aab7 100644 --- a/django/views/templates/technical_500.txt +++ b/django/views/templates/technical_500.txt @@ -30,8 +30,8 @@ In template {{ template_info.name }}, error at line {{ template_info.line }} Traceback (most recent call last): {% 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 }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} -{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %} +{% endif %}{% endifchanged %} {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} +{% if frame.context_line %} {% spaceless %}{{ frame.context_line }}{% endspaceless %}{% endif %}{% else %}{% if forloop.first %}None{% else %}Traceback: None{% endif %}{% endif %} {% endfor %} {% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %} {% if exception_value %}Exception Value: {{ exception_value }}{% endif %}{% endif %}{% endif %} diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index ccb870c238..a0da6e1adc 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -494,7 +494,7 @@ class ExceptionReporterTests(SimpleTestCase): reporter = ExceptionReporter(None, exc_type, exc_value, tb) frames = reporter.get_traceback_frames() - self.assertEqual(len(frames), 1) + self.assertEqual(len(frames), 2) html = reporter.get_traceback_html() self.assertInHTML('

    RuntimeError

    ', html) self.assertIn('
    Oops
    ', html) @@ -508,6 +508,52 @@ class ExceptionReporterTests(SimpleTestCase): 'exception occurred', html, ) + self.assertInHTML('
  • None
  • ', html) + self.assertIn('Traceback (most recent call last):\n None', html) + + text = reporter.get_traceback_text() + self.assertIn('Exception Type: RuntimeError', text) + self.assertIn('Exception Value: Oops', text) + self.assertIn('Traceback (most recent call last):\n None', text) + self.assertIn( + 'During handling of the above exception (My context), another ' + 'exception occurred', + text, + ) + + def test_mid_stack_exception_without_traceback(self): + try: + try: + raise RuntimeError('Inner Oops') + except Exception as exc: + new_exc = RuntimeError('My context') + new_exc.__context__ = exc + raise RuntimeError('Oops') from new_exc + except Exception: + exc_type, exc_value, tb = sys.exc_info() + reporter = ExceptionReporter(None, exc_type, exc_value, tb) + html = reporter.get_traceback_html() + self.assertInHTML('

    RuntimeError

    ', html) + self.assertIn('
    Oops
    ', html) + self.assertIn('Exception Type:', html) + self.assertIn('Exception Value:', html) + self.assertIn('

    Traceback ', html) + self.assertInHTML('
  • Traceback: None
  • ', html) + self.assertIn( + 'During handling of the above exception (Inner Oops), another ' + 'exception occurred:\n Traceback: None', + html, + ) + + text = reporter.get_traceback_text() + self.assertIn('Exception Type: RuntimeError', text) + self.assertIn('Exception Value: Oops', text) + self.assertIn('Traceback (most recent call last):', text) + self.assertIn( + 'During handling of the above exception (Inner Oops), another ' + 'exception occurred:\n Traceback: None', + text, + ) def test_reporting_of_nested_exceptions(self): request = self.rf.get('/test_view/') @@ -671,7 +717,7 @@ class ExceptionReporterTests(SimpleTestCase): self.assertIn('Request URL:', html) self.assertNotIn('Exception Type:', html) self.assertNotIn('Exception Value:', html) - self.assertNotIn('

    Traceback ', html) + self.assertIn('

    Traceback ', html) self.assertIn('

    Request information

    ', html) self.assertNotIn('

    Request data not supplied

    ', html) @@ -684,7 +730,7 @@ class ExceptionReporterTests(SimpleTestCase): self.assertNotIn('Request URL:', html) self.assertNotIn('Exception Type:', html) self.assertNotIn('Exception Value:', html) - self.assertNotIn('

    Traceback ', html) + self.assertIn('

    Traceback ', html) self.assertIn('

    Request information

    ', html) self.assertIn('

    Request data not supplied

    ', html)