diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index ba97077ce5..20048f5c55 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -220,7 +220,8 @@ class Signal(object): If any receiver raises an error (specifically any subclass of Exception), the error instance is returned as the result for that - receiver. + receiver. The traceback is always attached to the error at + ``__traceback__``. """ responses = [] if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: @@ -232,6 +233,8 @@ class Signal(object): try: response = receiver(signal=self, sender=sender, **named) except Exception as err: + if not hasattr(err, '__traceback__'): + err.__traceback__ = sys.exc_info()[2] responses.append((receiver, err)) else: responses.append((receiver, response)) diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index aa39fb2b7c..94a4f3ed74 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -153,7 +153,9 @@ Models Signals ^^^^^^^ -* ... +* Exceptions from the ``(receiver, exception)`` tuples returned by + :meth:`Signal.send_robust() ` now have + their traceback attached as a ``__traceback__`` attribute. Templates ^^^^^^^^^ diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt index 346d47d179..fb83258c9b 100644 --- a/docs/topics/signals.txt +++ b/docs/topics/signals.txt @@ -274,6 +274,11 @@ be notified of a signal in the face of an error. and ensures all receivers are notified of the signal. If an error occurs, the error instance is returned in the tuple pair for the receiver that raised the error. +.. versionadded:: 1.8 + + The tracebacks are present on the ``__traceback__`` attribute + of the errors returned when calling ``send_robust()``. + Disconnecting signals ===================== diff --git a/tests/dispatch/tests/test_dispatcher.py b/tests/dispatch/tests/test_dispatcher.py index 05b375f7d6..1bfa48aa80 100644 --- a/tests/dispatch/tests/test_dispatcher.py +++ b/tests/dispatch/tests/test_dispatcher.py @@ -3,6 +3,7 @@ import sys import time import unittest import weakref +from types import TracebackType from django.dispatch import Signal, receiver @@ -134,6 +135,8 @@ class DispatcherTests(unittest.TestCase): err = result[0][1] self.assertIsInstance(err, ValueError) self.assertEqual(err.args, ('this',)) + self.assertTrue(hasattr(err, '__traceback__')) + self.assertTrue(isinstance(err.__traceback__, TracebackType)) a_signal.disconnect(fails) self._testIsClean(a_signal)