在concurrent.futures中获取异常的原始行号 [英] Getting original line number for exception in 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屋!