|
@@ -1,5 +1,64 @@
|
|
|
"""celery.backends.base"""
|
|
|
from celery.timer import TimeoutTimer
|
|
|
+try:
|
|
|
+ import cPickle as pickle
|
|
|
+except ImportError:
|
|
|
+ import pickle
|
|
|
+import sys
|
|
|
+
|
|
|
+
|
|
|
+def find_nearest_pickleable_exception(exc):
|
|
|
+ """With an exception instance, iterate over its super classes (by mro)
|
|
|
+ and find the first super exception that is pickleable.
|
|
|
+
|
|
|
+ :param exc: An exception instance.
|
|
|
+ :rtype: :exc:`Exception`
|
|
|
+
|
|
|
+ """
|
|
|
+ for supercls in exc.__class__.mro():
|
|
|
+ try:
|
|
|
+ superexc = supercls(exc.args)
|
|
|
+ pickle.dumps(superexc)
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ return superexc
|
|
|
+ return exc
|
|
|
+
|
|
|
+
|
|
|
+class UnpickleableExceptionWrapper(Exception):
|
|
|
+ """Wraps unpickleable exceptions.
|
|
|
+
|
|
|
+ :param exc_module: see :attr:`exc_module`.
|
|
|
+
|
|
|
+ :param exc_cls_name: see :attr:`exc_cls_name`.
|
|
|
+
|
|
|
+ :param exc_args: The arguments for the original exception.
|
|
|
+
|
|
|
+ .. attribute:: exc_module
|
|
|
+
|
|
|
+ The module of the original exception.
|
|
|
+
|
|
|
+ .. attribute:: exc_cls_name
|
|
|
+
|
|
|
+ The name of the original exception class.
|
|
|
+
|
|
|
+ Example
|
|
|
+
|
|
|
+ >>> try:
|
|
|
+ ... something_raising_unpickleable_exc()
|
|
|
+ >>> except Exception, e:
|
|
|
+ ... exc = UnpickleableException(e.__class__.__module__,
|
|
|
+ ... e.__class__.__name__,
|
|
|
+ ... e.args)
|
|
|
+ ... pickle.dumps(exc) # Works fine.
|
|
|
+
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, exc_module, exc_cls_name, exc_args):
|
|
|
+ self.exc_module = exc_module
|
|
|
+ self.exc_cls = exc_cls_name
|
|
|
+ super(Exception, self).__init__(exc_module, exc_cls_name, exc_args)
|
|
|
|
|
|
|
|
|
class BaseBackend(object):
|
|
@@ -18,6 +77,19 @@ class BaseBackend(object):
|
|
|
"""Mark task as executed with failure. Stores the execption."""
|
|
|
return self.store_result(task_id, exc, status="FAILURE")
|
|
|
|
|
|
+ def prepare_exception(self, exc):
|
|
|
+ exc = find_nearest_pickleable_exception(exc)
|
|
|
+ try:
|
|
|
+ pickle.dumps(exc)
|
|
|
+ except pickle.PickleError:
|
|
|
+ excwrapper = UnpickleableExceptionWrapper(
|
|
|
+ exc.__class__.__module__,
|
|
|
+ exc.__class__.__name__,
|
|
|
+ exc.args)
|
|
|
+ return excwrapper
|
|
|
+ else:
|
|
|
+ return exc
|
|
|
+
|
|
|
def mark_as_retry(self, task_id, exc):
|
|
|
"""Mark task for retry."""
|
|
|
return self.store_result(task_id, exc, status="RETRY")
|