Python: Getting a traceback from a multiprocessing.Process Python: Getting a traceback from a multiprocessing.Process python python

Python: Getting a traceback from a multiprocessing.Process


Using tblib you can pass wrapped exceptions and reraise them later:

import tblib.pickling_supporttblib.pickling_support.install()from multiprocessing import Poolimport sysclass ExceptionWrapper(object):    def __init__(self, ee):        self.ee = ee        __, __, self.tb = sys.exc_info()    def re_raise(self):        raise self.ee.with_traceback(self.tb)        # for Python 2 replace the previous line by:        # raise self.ee, None, self.tb# example of how to use ExceptionWrapperdef inverse(i):    """ will fail for i == 0 """    try:        return 1.0 / i    except Exception as e:        return ExceptionWrapper(e)def main():    p = Pool(1)    results = p.map(inverse, [0, 1, 2, 3])    for result in results:        if isinstance(result, ExceptionWrapper):            result.re_raise()if __name__ == "__main__":    main()

So, if you catch an exception in your remote process, wrap it with ExceptionWrapper and then pass it back. Calling re_raise() in the main process will do the work.


Since multiprocessing does print the string contents of exceptions raised in child processes, you can wrap all your child process code in a try-except that catches any exceptions, formats the relavent stack traces, and raises a new Exception that holds all the relevant information in its string:

An example of a function I use with multiprocessing.map:

def run_functor(functor):    """    Given a no-argument functor, run it and return its result. We can     use this with multiprocessing.map and map it over a list of job     functors to do them.    Handles getting more than multiprocessing's pitiful exception output    """    try:        # This is where you do your actual work        return functor()    except:        # Put all exception text into an exception and raise that        raise Exception("".join(traceback.format_exception(*sys.exc_info())))

What you get is a stack trace with another formatted stack trace as the error message, which helps with debugging.


It seems to be difficult to made picklable the traceback object.But you can only send the 2 first items of sys.exc_info(), and a preformated traceback information with the traceback.extract_tb method :

import multiprocessingimport sysimport tracebackdef foo(pipe_to_parent):    try:        raise Exception('xxx')    except:        except_type, except_class, tb = sys.exc_info()        pipe_to_parent.send((except_type, except_class, traceback.extract_tb(tb)))to_child, to_self = multiprocessing.Pipe()process = multiprocessing.Process(target = foo, args = (to_self,))process.start()exc_info = to_child.recv()process.join()print exc_infoto_child.close()to_self.close()

which give you :

(<type 'exceptions.Exception'>, Exception('xxx',), [('test_tb.py', 7, 'foo', "raise Exception('xxx')")])

And then, you'll be able to grab more informations about the exception cause (filename, line number where exception raised, method name and the statement that raise the exception)