3

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

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

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

1 回答 1

1

Eventlet 的事件循环吞下所有异常,KeyboardInterrupt除了SystemExit. 检查wait()各种 eventlet hubs的实现。以下摘录来自选择集线器的 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()

You may want to turn off eventlet's DEBUG flag to prevent eventlet from printing the swallowed exception. When you want to handle the exception in main thread, you probably don't want to get confused by the duplicated traceback print.

import eventlet.debug
eventlet.debug.hub_exceptions(False)
于 2013-01-29T10:57:41.913 回答