Python:从multiprocessing.Process获取回溯

kni*_*nap 38 python exception process multiprocessing traceback

我试图从multiprocessing.Process中获取一个traceback对象.不幸的是,通过管道传递异常信息不起作用,因为无法对pickback对象进行pickle:

def foo(pipe_to_parent):
    try:
        raise Exception('xxx')
    except:
        pipe_to_parent.send(sys.exc_info())

to_child, to_self = multiprocessing.Pipe()
process = multiprocessing.Process(target = foo, args = (to_self,))
process.start()
exc_info = to_child.recv()
process.join()
print traceback.format_exception(*exc_info)
to_child.close()
to_self.close()
Run Code Online (Sandbox Code Playgroud)

追溯:

Traceback (most recent call last):
  File "/usr/lib/python2.6/multiprocessing/process.py", line 231, in _bootstrap
    self.run()
  File "/usr/lib/python2.6/multiprocessing/process.py", line 88, in run
    self._target(*self._args, **self._kwargs)
  File "foo", line 7, in foo
    to_parent.send(sys.exc_info())
PicklingError: Can't pickle <type 'traceback'>: attribute lookup __builtin__.traceback failed
Run Code Online (Sandbox Code Playgroud)

有没有其他方法来访问异常信息?我想避免传递格式化的字符串.

int*_*ect 29

由于multiprocessing打印子进程中引发的异常的字符串内容,您可以将所有子进程代码包装在try中 - 除了捕获任何异常,格式化相关堆栈跟踪,并引发一个新的Exception,其中包含其字符串中的所有相关信息:

我使用的函数示例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())))
Run Code Online (Sandbox Code Playgroud)

你得到的是一个堆栈跟踪与另一个格式化的堆栈跟踪作为错误消息,这有助于调试.


roc*_*ker 29

使用tblib您可以传递包装的异常并在以后重新加载它们:

import tblib.pickling_support
tblib.pickling_support.install()

from multiprocessing import Pool
import sys


class 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 how to use ExceptionWrapper

def 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()
Run Code Online (Sandbox Code Playgroud)

因此,如果您在远程进程中捕获异常,请将其包装ExceptionWrapper然后再传回.re_reraise在主要流程中调用将完成工作.

  • 我不确定为什么之前没有投票.它对我很好用!也许你应该包含一个如何使用`DelayedException`的例子 (2认同)

Céd*_*ien 14

似乎很难对traceback对象进行picklable.但是您只能sys.exc_info()使用traceback.extract_tb方法发送2个第一项和预先格式化的回溯信息:

import multiprocessing
import sys
import traceback

def 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_info
to_child.close()
to_self.close()
Run Code Online (Sandbox Code Playgroud)

哪个给你:

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

然后,您将能够获取有关异常原因的更多信息(文件名,引发异常的行号,方法名称和引发异常的语句)


Syr*_*jor 7

Python 3

在Python 3中,现在返回完全回溯的get方法multiprocessing.pool.Async,请参阅http://bugs.python.org/issue13831.

Python 2

使用traceback.format_exc(表示格式化的exption)来获取回溯字符串.如下所示制作装饰器会更加方便.

def full_traceback(func):
    import traceback, functools
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            msg = "{}\n\nOriginal {}".format(e, traceback.format_exc())
            raise type(e)(msg)
    return wrapper
Run Code Online (Sandbox Code Playgroud)

例:

def func0():
    raise NameError("func0 exception")

def func1():
    return func0()

# Key is here!
@full_traceback
def main(i):
    return func1()

if __name__ == '__main__':
    from multiprocessing import Pool
    pool = Pool(4)
    try:
        results = pool.map_async(main, range(5)).get(1e5)
    finally:
        pool.close()
        pool.join()
Run Code Online (Sandbox Code Playgroud)

回溯装饰:

Traceback (most recent call last):
  File "bt.py", line 34, in <module>
    results = pool.map_async(main, range(5)).get(1e5)
  File "/opt/anaconda/lib/python2.7/multiprocessing/pool.py", line 567, in get
    raise self._value
NameError: Exception in func0

Original Traceback (most recent call last):
  File "bt.py", line 13, in wrapper
    return func(*args, **kwargs)
  File "bt.py", line 27, in main
    return func1()
  File "bt.py", line 23, in func1
    return func0()
  File "bt.py", line 20, in func0
    raise NameError("Exception in func0")
NameError: Exception in func0
Run Code Online (Sandbox Code Playgroud)

没有装饰器的回溯:

Traceback (most recent call last):
  File "bt.py", line 34, in <module>
    results = pool.map_async(main, range(5)).get(1e5)
  File "/opt/anaconda/lib/python2.7/multiprocessing/pool.py", line 567, in get
    raise self._value
NameError: Exception in func0
Run Code Online (Sandbox Code Playgroud)