Browse Source

Trace: Must also find pickleable type

Ask Solem 11 years ago
parent
commit
812ef9be3b
2 changed files with 31 additions and 13 deletions
  1. 7 2
      celery/task/trace.py
  2. 24 11
      celery/utils/serialization.py

+ 7 - 2
celery/task/trace.py

@@ -32,7 +32,10 @@ from celery.datastructures import ExceptionInfo
 from celery.exceptions import Ignore, RetryTaskError
 from celery.utils.log import get_logger
 from celery.utils.objects import mro_lookup
-from celery.utils.serialization import get_pickleable_exception
+from celery.utils.serialization import (
+    get_pickleable_exception,
+    get_pickleable_etype,
+)
 
 _logger = get_logger(__name__)
 
@@ -100,7 +103,9 @@ class TraceInfo(object):
         type_, _, tb = sys.exc_info()
         try:
             exc = self.retval
-            einfo = ExceptionInfo((type_, get_pickleable_exception(exc), tb))
+            einfo = ExceptionInfo()
+            einfo.exception = get_pickleable_exception(einfo.exception)
+            einfo.type = get_pickleable_etype(einfo.type)
             if store_errors:
                 task.backend.mark_as_failure(req.id, exc, einfo.traceback)
             task.on_failure(exc, req.id, req.args, req.kwargs, einfo)

+ 24 - 11
celery/utils/serialization.py

@@ -29,7 +29,8 @@ def subclass_exception(name, parent, module):  # noqa
     return type(name, (parent,), {'__module__': module})
 
 
-def find_nearest_pickleable_exception(exc):
+def find_pickleable_exception(exc, loads=pickle.loads,
+                              dumps=pickle.dumps):
     """With an exception instance, iterate over its super classes (by mro)
     and find the first super exception that is pickleable.  It does
     not go below :exc:`Exception` (i.e. it skips :exc:`Exception`,
@@ -44,7 +45,19 @@ def find_nearest_pickleable_exception(exc):
     :rtype :exc:`Exception`:
 
     """
-    cls = exc.__class__
+    exc_args = getattr(exc, 'args', [])
+    for supercls in itermro(exc.__class__, unwanted_base_classes):
+        try:
+            superexc = supercls(*exc_args)
+            loads(dumps(superexc))
+        except:
+            pass
+        else:
+            return superexc
+find_nearest_pickleable_exception = find_pickleable_exception  # XXX compat
+
+
+def itermro(cls, stop):
     getmro_ = getattr(cls, 'mro', None)
 
     # old-style classes doesn't have mro()
@@ -56,18 +69,11 @@ def find_nearest_pickleable_exception(exc):
         getmro_ = lambda: inspect.getmro(cls)
 
     for supercls in getmro_():
-        if supercls in unwanted_base_classes:
+        if supercls in stop:
             # only BaseException and object, from here on down,
             # we don't care about these.
             return
-        try:
-            exc_args = getattr(exc, 'args', [])
-            superexc = supercls(*exc_args)
-            pickle.loads(pickle.dumps(superexc))
-        except:
-            pass
-        else:
-            return superexc
+        yield
 
 
 def create_exception_cls(name, module, parent=None):
@@ -150,6 +156,13 @@ def get_pickleable_exception(exc):
     return UnpickleableExceptionWrapper.from_exception(exc)
 
 
+def get_pickleable_etype(cls, loads=pickle.loads, dumps=pickle.dumps):
+    try:
+        loads(dumps(cls))
+    except:
+        return Exception
+
+
 def get_pickled_exception(exc):
     """Get original exception from exception pickled using
     :meth:`get_pickleable_exception`."""