40

multiprocessing.Processからトレースバックオブジェクトを取得しようとしています。残念ながら、トレースバックオブジェクトをピクルスできないため、パイプを介して例外情報を渡すことはできません。

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()

トレースバック:

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

例外情報にアクセスする別の方法はありますか?フォーマットされた文字列を渡さないようにしたいと思います。

4

6 に答える 6

35

を使用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 of 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()

したがって、リモートプロセスで例外をキャッチした場合は、それをラップしてExceptionWrapperから返します。re_raise()メインプロセスを呼び出すと、作業が行われます。

于 2014-09-29T09:13:55.380 に答える
30

子プロセスで発生した例外の文字列コンテンツを出力するため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())))

取得するのは、エラーメッセージとして別のフォーマットされたスタックトレースを含むスタックトレースです。これは、デバッグに役立ちます。

于 2013-05-17T22:33:34.413 に答える
14

トレースバックオブジェクトをピック可能にするのは難しいようです。ただし、送信できるのは、の最初の2つの項目sys.exc_info()と、traceback.extract_tbメソッドを使用して事前にフォーマットされたトレースバック情報のみです。

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()

あなたに与える:

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

次に、例外の原因に関する詳細情報(ファイル名、例外が発生した行番号、メソッド名、および例外を発生させるステートメント)を取得できるようになります。

于 2011-05-25T15:30:39.327 に答える
10

Python 3

Python 3では、完全なトレースバックを返すgetメソッドになりました。http://bugs.python.org/issue13831を参照してください。multiprocessing.pool.Async

Python 2

traceback.format_exc(フォーマットされたexpetionを意味する)を使用して、トレースバック文字列を取得します。以下のようにデコレータを作成すると、はるかに便利になります。

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

例:

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()

デコレータを使用たトレースバック:

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

デコレータなしのトレースバック:

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
于 2017-04-05T06:29:56.787 に答える
4

これは、この優れた回答のバリエーションです。どちらも、トレースバックを格納するためにtblibに依存しています。

ただし、(OPによって要求されたように)例外オブジェクトを返す代わりに、worker関数をそのままにして、try/でラップしexceptて、再発生のために例外を格納することができます。

import tblib.pickling_support
tblib.pickling_support.install()

import sys

class DelayedException(Exception):

    def __init__(self, ee):
        self.ee = ee
        __,  __, self.tb = sys.exc_info()
        super(DelayedException, self).__init__(str(ee))

    def re_raise(self):
        raise self.ee, None, self.tb

def worker():
    try:
        raise ValueError('Something went wrong.')
    except Exception as e:
        raise DelayedException(e)


if __name__ == '__main__':

    import multiprocessing

    pool = multiprocessing.Pool()
    try:
        pool.imap(worker, [1, 2, 3])
    except DelayedException as e:
        e.re_raise()
于 2017-02-02T10:53:32.677 に答える
0

@Syrtis Majorおよび@interfectと同じソリューションですが、Python3.6でテストされています。

import sys
import traceback
import functools

def catch_remote_exceptions(wrapped_function):
    """ https://stackoverflow.com/questions/6126007/python-getting-a-traceback """

    @functools.wraps(wrapped_function)
    def new_function(*args, **kwargs):
        try:
            return wrapped_function(*args, **kwargs)

        except:
            raise Exception( "".join(traceback.format_exception(*sys.exc_info())) )

    return new_function

使用法:

class ProcessLocker(object):
    @catch_remote_exceptions
    def __init__(self):
        super().__init__()

    @catch_remote_exceptions
    def create_process_locks(self, total_processes):
        self.process_locks = []
        # ...
于 2019-09-06T18:49:31.843 に答える