Eventlet's event loop swallows all exceptions, barring KeyboardInterrupt
and SystemExit
. Check the wait()
implementation of various eventlet hubs. The following excerpt is from 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()
To work around this, you can pass the exception info as value and deal with it later in the main thread.
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)