gevent 中的 ThreadPool 异常不会返回
ThreadPool exceptions in gevent do not get returned
我在 Python 2.7 上使用 gevent 来做一些多线程工作。但是,我无法捕获生成方法中引发的异常。我在 returned AsyncResult
对象上使用 .get()
方法(在线程池上调用 .spawn()
方法时 returned)。
根据 gevent 文档:http://www.gevent.org/gevent.event.html#gevent.event.AsyncResult.get:
get(block=True, timeout=None)
Return the stored value or raise the exception.
If this instance already holds a value or an exception, return or raise it immediately.
但是,没有 return 异常,.get()
returns NoneType
。此外,在控制台中,打印出异常详细信息。相反,.get()
应该 return 返回异常。
下面是一些示例代码和相应的输出,展示了这一点:
from gevent import threadpool
def this_will_fail():
raise Exception('This will fail')
result_list = []
for i in range(1,5):
tpool = threadpool.ThreadPool(5)
result_list.append(tpool.spawn(this_will_fail))
tpool.join()
for result in result_list:
try:
returned_object = result.get()
print type(returned_object)
except Exception as e:
print 'This is not running for some reason...'
输出:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c79e50 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99210 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99410 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99610 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
<type 'NoneType'>
<type 'NoneType'>
<type 'NoneType'>
<type 'NoneType'>
我是否遗漏了一些明显的东西,或者这是 gevent 中的错误?
编辑: concurrent.futures
没有这个问题。虽然这对我的用例来说是一个可以接受的解决方案,但我仍然想了解为什么 gevent 没有正确 return 异常。
在这里,我们遇到了与 gevent 线程池相同的问题。我们正在使用一种解决方案,将函数包装到 return 异常作为结果,并在我们想要使用 AsynResult 的结果时再次引发它。
class _ExceptionWrapper:
def __init__(self, exception, error_string, tb):
self.exception = exception
self.error_string = error_string
self.tb = tb
class wrap_errors(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
func = self.func
try:
return func(*args, **kwargs)
except:
return _ExceptionWrapper(*sys.exc_info())
def __str__(self):
return str(self.func)
def __repr__(self):
return repr(self.func)
def __getattr__(self, item):
return getattr(self.func, item)
def get_with_exception(g, block=True, timeout=None):
result = g._get(block, timeout)
if isinstance(result, _ExceptionWrapper):
# raise the exception using the caller context
raise result.error_string, None, result.tb
else:
return result
def spawn(fn, *args, **kwargs):
# wrap the function
fn = wrap_errors(fn)
g = threadpool.spawn(fn, *args, **kwargs)
# and the asynresult
g._get = g.get
g.get = types.MethodType(get_with_exception, g)
return g
我在 Python 2.7 上使用 gevent 来做一些多线程工作。但是,我无法捕获生成方法中引发的异常。我在 returned AsyncResult
对象上使用 .get()
方法(在线程池上调用 .spawn()
方法时 returned)。
根据 gevent 文档:http://www.gevent.org/gevent.event.html#gevent.event.AsyncResult.get:
get(block=True, timeout=None)
Return the stored value or raise the exception.
If this instance already holds a value or an exception, return or raise it immediately.
但是,没有 return 异常,.get()
returns NoneType
。此外,在控制台中,打印出异常详细信息。相反,.get()
应该 return 返回异常。
下面是一些示例代码和相应的输出,展示了这一点:
from gevent import threadpool
def this_will_fail():
raise Exception('This will fail')
result_list = []
for i in range(1,5):
tpool = threadpool.ThreadPool(5)
result_list.append(tpool.spawn(this_will_fail))
tpool.join()
for result in result_list:
try:
returned_object = result.get()
print type(returned_object)
except Exception as e:
print 'This is not running for some reason...'
输出:
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c79e50 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99210 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99410 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/gevent/threadpool.py", line 193, in _worker
value = func(*args, **kwargs)
File "<stdin>", line 2, in this_will_fail
Exception: This will fail
(<ThreadPool at 0x107c99610 0/1/5>, <function this_will_fail at 0x107c848c0>) failed with Exception
<type 'NoneType'>
<type 'NoneType'>
<type 'NoneType'>
<type 'NoneType'>
我是否遗漏了一些明显的东西,或者这是 gevent 中的错误?
编辑: concurrent.futures
没有这个问题。虽然这对我的用例来说是一个可以接受的解决方案,但我仍然想了解为什么 gevent 没有正确 return 异常。
在这里,我们遇到了与 gevent 线程池相同的问题。我们正在使用一种解决方案,将函数包装到 return 异常作为结果,并在我们想要使用 AsynResult 的结果时再次引发它。
class _ExceptionWrapper:
def __init__(self, exception, error_string, tb):
self.exception = exception
self.error_string = error_string
self.tb = tb
class wrap_errors(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
func = self.func
try:
return func(*args, **kwargs)
except:
return _ExceptionWrapper(*sys.exc_info())
def __str__(self):
return str(self.func)
def __repr__(self):
return repr(self.func)
def __getattr__(self, item):
return getattr(self.func, item)
def get_with_exception(g, block=True, timeout=None):
result = g._get(block, timeout)
if isinstance(result, _ExceptionWrapper):
# raise the exception using the caller context
raise result.error_string, None, result.tb
else:
return result
def spawn(fn, *args, **kwargs):
# wrap the function
fn = wrap_errors(fn)
g = threadpool.spawn(fn, *args, **kwargs)
# and the asynresult
g._get = g.get
g.get = types.MethodType(get_with_exception, g)
return g