diff --git a/pcl_expect/remote_exception.py b/pcl_expect/remote_exception.py new file mode 100644 index 0000000000000000000000000000000000000000..0ad0cf343e9c29fa2ec5b9e1ca5c686816011b55 --- /dev/null +++ b/pcl_expect/remote_exception.py @@ -0,0 +1,96 @@ +"""Exception handling for remote processes. + + If you have a controller process that spawns a child process, and + an unexpected exception occurs in the child process, you often want + to report it via the controller process. Perhaps it can be handled + in the controller process. At the very least, you want to get a + traceback from the child process. + + This module defines a new exception, Remote, which encapsulates any + other exception (as long as the other exception only contains data + that can be cPickled). It also provides two functions: + + - serialize: call this in an exception handler in the child + process. It will create a Remote exception based on + sys.exc_info(), serialize it, and return it as a string. + + - re_raise: when given a string produced by serialize(), it will + raise the Remote exception. + + This module does not attempt to transfer the serialized exception + from the child process to the controller process. It is assumed + that you already have a channel where that can be done. + + Typical usage: + + In the client: + + try: + # Code that can raise an exception + except: + x = remote_exception.serialize() + # Somehow send x to the controller. + + In the server: + + # Somehow receive the serialized string into x. + remote_exception.re_raise(x) + + See the test code at the end of this module for working demo code. +""" + +import sys +import cPickle +import traceback + +class Remote(Exception): + """An exception occured in a remote process. + + This exception defines these attributes: + + - exc_type -- The type of the original exception. + - exc_value -- The value of the original exception. + - traceback -- A preformatted traceback string, ready for + printing (or writing to a log file, et c). + """ + + def __init__(self): + """Create a Remote exception. + + The exception data will be gathered using sys.exc_info(), + so this must be created inside an except clause. + """ + self.exc_type, self.exc_value, tb = sys.exc_info() + self.traceback = ''.join(traceback.format_exception( + self.exc_type, self.exc_value, tb)) + Exception.__init__(self, self.exc_type.__name__, str(self.exc_value)) + +def serialize(): + """Create a Remote exception, serialize it, and return the string. + + This must be called from within an except clause. + + """ + + x = Remote() + return cPickle.dumps(x, cPickle.HIGHEST_PROTOCOL) + +def re_raise(s): + """Decode a serialized Remote exception, and raise it.""" + + raise cPickle.loads(s) + +# Demo code follows. +if __name__ == "__main__": + try: + x + except: + y = serialize() + + try: + re_raise(y) + except Remote, z: + print "Remote exception follows:" + print z.traceback + print "The exception was re-generated here:" + print ''.join(traceback.format_exception(*sys.exc_info()))