Browse Source

Fixed #16245 -- Included traceback in send_robust()'s response

Exceptions from the (receiver, exception) tuples returned by
``send_robust()`` now have always their traceback attached as their
``__traceback__`` argument.
Unai Zalakain 11 years ago
parent
commit
ebb0279f4a

+ 4 - 1
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))

+ 3 - 1
docs/releases/1.8.txt

@@ -153,7 +153,9 @@ Models
 Signals
 ^^^^^^^
 
-* ...
+* Exceptions from the ``(receiver, exception)`` tuples returned by
+  :meth:`Signal.send_robust() <django.dispatch.Signal.send_robust>` now have
+  their traceback attached as a ``__traceback__`` attribute.
 
 Templates
 ^^^^^^^^^

+ 5 - 0
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
 =====================
 

+ 3 - 0
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)