在concurrent.futures中获取异常的原始行号 [英] Getting original line number for exception in concurrent.futures

查看:601
本文介绍了在concurrent.futures中获取异常的原始行号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用concurrent.futures的示例(2.7的backport):

Example of using concurrent.futures (backport for 2.7):

import concurrent.futures  # line 01
def f(x):  # line 02
    return x * x  # line 03
data = [1, 2, 3, None, 5]  # line 04
with concurrent.futures.ThreadPoolExecutor(len(data)) as executor:  # line 05
    futures = [executor.submit(f, n) for n in data]  # line 06
    for future in futures:  # line 07
        print(future.result())  # line 08

输出:

1
4
9
Traceback (most recent call last):
  File "C:\test.py", line 8, in <module>
    print future.result()  # line 08
  File "C:\dev\Python27\lib\site-packages\futures-2.1.4-py2.7.egg\concurrent\futures\_base.py", line 397, in result
    return self.__get_result()
  File "C:\dev\Python27\lib\site-packages\futures-2.1.4-py2.7.egg\concurrent\futures\_base.py", line 356, in __get_result
    raise self._exception
TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType'

String ... \\ \\ _base.py,行356,在__get_result是不是我期望看到的端点。是否可能得到实线抛出异常?类似:

String "...\_base.py", line 356, in __get_result" is not endpoint I expected to see. Is it possible to get real line where exception was thrown? Something like:

  File "C:\test.py", line 3, in f
    return x * x  # line 03

Python3似乎在这种情况下显示正确的行号为什么不能python2.7? >

Python3 seems to show correct line number in this case. Why can't python2.7? And is there any workaround?

推荐答案

我认为原来的异常traceback在ThreadPoolExecutor代码中丢失。它存储异常,然后重新提出它。这里有一个解决方案。您可以使用 traceback 模块将原始异常消息和回溯函数 f 保存到字符串中。然后用此错误消息引发异常,现在包含 f 的行号等。运行 f 的代码可以包装在 try ... 除了块,捕获ThreadPoolExecutor引发的异常, ,其中包含原始追踪。

I think the original exception traceback gets lost in the ThreadPoolExecutor code. It stores the exception and then re-raises it later. Here is one solution. You can use the traceback module to store the original exception message and traceback from your function f into a string. Then raise an exception with this error message, which now contains the line number etc of f. The code that runs f can be wrapped in a try...except block, which catches the exception raised from ThreadPoolExecutor, and prints the message, which contains the original traceback.

以下代码适用于我。我认为这个解决方案是有点hacky,并希望能够恢复原始的traceback,但我不知道如果这是可能的。

The code below works for me. I think this solution is a little hacky, and would prefer to be able to recover the original traceback, but I'm not sure if that is possible.

import concurrent.futures
import sys,traceback


def f(x):
    try:
        return x * x
    except Exception, e:
        tracebackString = traceback.format_exc(e)
        raise StandardError, "\n\nError occurred. Original traceback is\n%s\n" %(tracebackString)



data = [1, 2, 3, None, 5]  # line 10

with concurrent.futures.ThreadPoolExecutor(len(data)) as executor:  # line 12
    try:
        futures = [executor.submit(f, n) for n in data]  # line 13
        for future in futures:  # line 14
           print(future.result())  # line 15
    except StandardError, e:
        print "\n"
        print e.message
        print "\n"

这在python2.7中给出以下输出:

This gives the following output in python2.7:

1
4
9




Error occurred. Original traceback is
Traceback (most recent call last):
File "thread.py", line 8, in f
   return x * x
TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType'

原始代码给出正确位置的原因在Python 3而不是2.7中运行是在Python 3异常中携带traceback作为属性,并且当重新提高异常时,traceback被扩展而不是替换。以下示例说明了这一点:

The reason your original code gives the right location when run in Python 3 and not 2.7 is that in Python 3 exceptions carry the traceback as an attribute, and when re-raising an exception, the traceback is extended rather than replaced. The example below illustrates this:

def A():
    raise BaseException("Fish")

def B():
    try:
        A()
    except BaseException as e:
        raise e

B()

我在 python 2.7 python 3.1 。在2.7中,输出如下:

I ran this in python 2.7 and python 3.1. In 2.7 the output is as follows:

Traceback (most recent call last):
  File "exceptions.py", line 11, in <module>
    B()
  File "exceptions.py", line 9, in B
    raise e
BaseException: Fish

ie异常最初从 A 抛出的事实不会记录在最终输出中。当我使用 python 3.1 运行时,我得到:

i.e. the fact that the exception was originally thrown from A is not recorded in the eventual output. When I run with python 3.1 I get this:

Traceback (most recent call last):
  File "exceptions.py", line 11, in <module>
    B()
  File "exceptions.py", line 9, in B
    raise e
  File "exceptions.py", line 7, in B
    A()
  File "exceptions.py", line 3, in A
    raise BaseException("Fish")
BaseException: Fish

这是更好的。如果我在 B 中的except块中用 raise 替换 raise e strong> python2.7 提供完整的回溯。我的猜测是,当支持 python2.7 的模块时,忽略了异常传播的差异。

which is better. If I replace raise e with just raise in the except block in B, then python2.7 gives the complete traceback. My guess is that when back-porting this module for python2.7 the differences in exception propagating were overlooked.

这篇关于在concurrent.futures中获取异常的原始行号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆