Browse Source

celery.serialization: Use inspect.getmro() to get the bases of an old-style exception.

Also add StandardError to unwanted_base_classes
Ask Solem 15 years ago
parent
commit
9d88df806f
1 changed files with 34 additions and 21 deletions
  1. 34 21
      celery/serialization.py

+ 34 - 21
celery/serialization.py

@@ -1,23 +1,21 @@
-import sys
-import types
 try:
     import cPickle as pickle
 except ImportError:
     import pickle
+import inspect
+import sys
+import types
+
 from copy import deepcopy
 
+# BaseException was introduced in Python 2.5.
 try:
     _error_bases = (BaseException, )
 except NameError:
     _error_bases = (SystemExit, KeyboardInterrupt)
 
+unwanted_base_classes = (StandardError, Exception) + _error_bases + (object, )
 
-def is_unwanted_exception_cls(exc_cls):
-    unwanted_classes = (Exception, ) + _error_bases + (object, )
-    for unwanted_cls in unwanted_classes:
-        if exc_cls is unwanted_cls:
-            return True
-    return False
 
 if sys.version_info < (2, 5):
 
@@ -44,13 +42,24 @@ def find_nearest_pickleable_exception(exc):
     :rtype: :exc:`Exception`
 
     """
+    cls = exc.__class__
+    getmro_ = getattr(cls, "mro", None)
 
-    mro_ = getattr(exc.__class__, "mro", lambda: [])
-    for supercls in mro_():
-        if is_unwanted_exception_cls(supercls):
+    # old-style classes doesn't have mro()
+    if not getmro_:
+        # all Py2.4 exceptions has a baseclass.
+        if not getattr(cls, "__bases__", ()):
+            return
+        # Use inspect.getmro() to traverse bases instead.
+        getmro_ = lambda: inspect.getmro(cls)
+
+
+    for supercls in getmro_():
+        print("SUPERCLS: %s" % (supercls, ))
+        if supercls in unwanted_base_classes:
             # only BaseException and object, from here on down,
             # we don't care about these.
-            return None
+            return
         try:
             exc_args = getattr(exc, "args", [])
             superexc = supercls(*exc_args)
@@ -59,7 +68,7 @@ def find_nearest_pickleable_exception(exc):
             pass
         else:
             return superexc
-    return None
+    return
 
 
 def create_exception_cls(name, module, parent=None):
@@ -108,6 +117,16 @@ class UnpickleableExceptionWrapper(Exception):
         self.exc_args = exc_args
         Exception.__init__(self, exc_module, exc_cls_name, exc_args)
 
+    @classmethod
+    def from_exception(cls, exc):
+        return cls(exc.__class__.__module__,
+                   exc.__class__.__name__,
+                   getattr(exc, "args", []))
+
+    def restore(self):
+        return create_exception_cls(self.exc_cls_name,
+                                    self.exc_module)(*self.exc_args)
+
 
 def get_pickleable_exception(exc):
     """Make sure exception is pickleable."""
@@ -118,11 +137,7 @@ def get_pickleable_exception(exc):
     try:
         pickle.dumps(deepcopy(exc))
     except Exception:
-        excwrapper = UnpickleableExceptionWrapper(
-                        exc.__class__.__module__,
-                        exc.__class__.__name__,
-                        getattr(exc, "args", []))
-        return excwrapper
+        return UnpickleableExceptionWrapper.from_exception(exc)
     return exc
 
 
@@ -130,7 +145,5 @@ def get_pickled_exception(exc):
     """Get original exception from exception pickled using
     :meth:`get_pickleable_exception`."""
     if isinstance(exc, UnpickleableExceptionWrapper):
-        exc_cls = create_exception_cls(exc.exc_cls_name,
-                                       exc.exc_module)
-        return exc_cls(*exc.exc_args)
+        return exc.restore()
     return exc