使用eventlet.GreenPool.spawn时,如何在主线程中引发异常

时间:2012-12-17 16:59:17

标签: python asynchronous eventlet green-threads

我使用eventlet.GreenPool.spawn运行一些任务,然后等待所有greanthreads完成。我知道会引发一个异常 - 如何捕获该异常并将其抛入主线程?我很确定这很容易,但我完全错过了一些东西。

这是一个例子(失败了,我希望它成功)

import unittest
import eventlet


def broken_fetch(url):
    print " Raising exception "
    raise RuntimeError


class TestPool(unittest.TestCase):

    def test_error_is_bubbled_up(self):
        with self.assertRaises(RuntimeError):
            pool = eventlet.GreenPool(100)
            urls = ['http://google.com/', 'http://example.com/']
            for url in urls:
                pool.spawn(broken_fetch, url)
            pool.waitall()

if __name__ == '__main__':
    unittest.main()

它的输出:

> python errors.py
Raising exception 
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/eventlet/hubs/hub.py", line 336, in fire_timers
    timer()
File "/usr/local/lib/python2.7/site-packages/eventlet/hubs/timer.py", line 56, in __call__
    cb(*args, **kw)
File "/usr/local/lib/python2.7/site-packages/eventlet/greenthread.py", line 192, in main
    result = function(*args, **kwargs)
File "errors.py", line 10, in broken_fetch
    raise RuntimeError
RuntimeError
Raising exception 
Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/eventlet/hubs/hub.py", line 336, in fire_timers
    timer()
File "/usr/local/lib/python2.7/site-packages/eventlet/hubs/timer.py", line 56, in __call__
    cb(*args, **kw)
File "/usr/local/lib/python2.7/site-packages/eventlet/greenthread.py", line 192, in main
    result = function(*args, **kwargs)
File "errors.py", line 10, in broken_fetch
    raise RuntimeError
RuntimeError
F
======================================================================
FAIL: test_error_is_bubbled_up (__main__.TestPool)
----------------------------------------------------------------------
Traceback (most recent call last):
File "errors.py", line 21, in test_error_is_bubbled_up
    pool.waitall()
AssertionError: RuntimeError not raised

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)

1 个答案:

答案 0 :(得分:1)

Eventlet的事件循环吞下所有异常,禁止KeyboardInterruptSystemExit。检查各种eventlet hubswait()实现。以下摘录来自select hub's wait()

for listeners, events in ((readers, r), (writers, w)):
    for fileno in events:
        try:
            listeners.get(fileno, noop).cb(fileno)
        except self.SYSTEM_EXCEPTIONS:
            raise
        except:
            self.squelch_exception(fileno, sys.exc_info())
            clear_sys_exc_info()

要解决此问题,您可以将异常信息作为值传递,稍后在主线程中处理它。

import unittest2
import sys
import eventlet


def broken_fetch(url):
    print " Raising exception "
    try:
        raise RuntimeError
    except:
        return sys.exc_info()

class TestPool(unittest2.TestCase):

    def test_error_is_bubbled_up(self):
        with self.assertRaises(RuntimeError):
            pool = eventlet.GreenPool(100)
            urls = ['http://google.com/', 'http://example.com/']
            for exc_info in pool.imap(broken_fetch, urls):
                if exc_info is not None:
                    exc_class, value, tb = exc_info
                    raise exc_class, value, tb

if __name__ == '__main__':
    unittest2.main()

您可能希望关闭eventlet的DEBUG标志以防止eventlet打印吞下的异常。当你想在主线程中处理异常时,你可能不希望被重复的回溯打印弄糊涂。

import eventlet.debug
eventlet.debug.hub_exceptions(False)