Instead of keeping a reference to the unhandled exception, keep only a string
describing it, and account for the possibility of receiving that same string
in __init__() upon unpickling.

If references to unhandled exceptions are kept in the Unhandled* wrappers, the
unhandled exceptions too are subjected to pickling horrors: their __init__()
methods are called upon unpickling and the .args tuple is passed as arguments
to them.  This means that every exception that passes to its base constructor
something that differs from the parameters passed to its __init__() method has
to explicitly store these parameters in .args.  It's better to deal with this
problem in the Unhandled* wrappers than to have to deal with it in all other
exception classes.

This weird Python behavior is apparently a known issue:
http://bugs.python.org/issue1692335

Signed-off-by: Michael Goldish <mgold...@redhat.com>
---
 client/common_lib/error.py |   69 +++++++++++++++++++-------------------------
 1 files changed, 30 insertions(+), 39 deletions(-)

diff --git a/client/common_lib/error.py b/client/common_lib/error.py
index 76ccc77..0c5641c 100644
--- a/client/common_lib/error.py
+++ b/client/common_lib/error.py
@@ -181,21 +181,18 @@ class JobError(AutotestError):
 class UnhandledJobError(JobError):
     """Indicates an unhandled error in a job."""
     def __init__(self, unhandled_exception):
-        JobError.__init__(self, unhandled_exception)
-        self.unhandled_exception = unhandled_exception
-        self.traceback = traceback.format_exc()
-
-    def __str__(self):
-        if isinstance(self.unhandled_exception, JobError):
-            return JobError.__str__(self.unhandled_exception)
+        if isinstance(unhandled_exception, JobError):
+            JobError.__init__(self, *unhandled_exception.args)
+        elif isinstance(unhandled_exception, str):
+            JobError.__init__(self, unhandled_exception)
         else:
             msg = "Unhandled %s: %s"
-            msg %= (self.unhandled_exception.__class__.__name__,
-                    self.unhandled_exception)
-            if not isinstance(self.unhandled_exception, AutotestError):
-                msg += _context_message(self.unhandled_exception)
-            msg += "\n" + self.traceback
-            return msg
+            msg %= (unhandled_exception.__class__.__name__,
+                    unhandled_exception)
+            if not isinstance(unhandled_exception, AutotestError):
+                msg += _context_message(unhandled_exception)
+            msg += "\n" + traceback.format_exc()
+            JobError.__init__(self, msg)
 
 
 class TestBaseException(AutotestError):
@@ -229,41 +226,35 @@ class TestWarn(TestBaseException):
 class UnhandledTestError(TestError):
     """Indicates an unhandled error in a test."""
     def __init__(self, unhandled_exception):
-        TestError.__init__(self, unhandled_exception)
-        self.unhandled_exception = unhandled_exception
-        self.traceback = traceback.format_exc()
-
-    def __str__(self):
-        if isinstance(self.unhandled_exception, TestError):
-            return TestError.__str__(self.unhandled_exception)
+        if isinstance(unhandled_exception, TestError):
+            TestError.__init__(self, *unhandled_exception.args)
+        elif isinstance(unhandled_exception, str):
+            TestError.__init__(self, unhandled_exception)
         else:
             msg = "Unhandled %s: %s"
-            msg %= (self.unhandled_exception.__class__.__name__,
-                    self.unhandled_exception)
-            if not isinstance(self.unhandled_exception, AutotestError):
-                msg += _context_message(self.unhandled_exception)
-            msg += "\n" + self.traceback
-            return msg
+            msg %= (unhandled_exception.__class__.__name__,
+                    unhandled_exception)
+            if not isinstance(unhandled_exception, AutotestError):
+                msg += _context_message(unhandled_exception)
+            msg += "\n" + traceback.format_exc()
+            TestError.__init__(self, msg)
 
 
 class UnhandledTestFail(TestFail):
     """Indicates an unhandled fail in a test."""
     def __init__(self, unhandled_exception):
-        TestFail.__init__(self, unhandled_exception)
-        self.unhandled_exception = unhandled_exception
-        self.traceback = traceback.format_exc()
-
-    def __str__(self):
-        if isinstance(self.unhandled_exception, TestFail):
-            return TestFail.__str__(self.unhandled_exception)
+        if isinstance(unhandled_exception, TestFail):
+            TestFail.__init__(self, *unhandled_exception.args)
+        elif isinstance(unhandled_exception, str):
+            TestFail.__init__(self, unhandled_exception)
         else:
             msg = "Unhandled %s: %s"
-            msg %= (self.unhandled_exception.__class__.__name__,
-                    self.unhandled_exception)
-            if not isinstance(self.unhandled_exception, AutotestError):
-                msg += _context_message(self.unhandled_exception)
-            msg += "\n" + self.traceback
-            return msg
+            msg %= (unhandled_exception.__class__.__name__,
+                    unhandled_exception)
+            if not isinstance(unhandled_exception, AutotestError):
+                msg += _context_message(unhandled_exception)
+            msg += "\n" + traceback.format_exc()
+            TestFail.__init__(self, msg)
 
 
 class CmdError(TestError):
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to