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 <mgoldish@xxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html