diff --git a/eventlet/support/__init__.py b/eventlet/support/__init__.py index 55a5288..4ee9121 100644 --- a/eventlet/support/__init__.py +++ b/eventlet/support/__init__.py @@ -1,4 +1,8 @@ import sys + +from eventlet.support import greenlets + + def get_errno(exc): """ Get the error code out of socket.error objects. socket.error in <2.5 does not have errno attribute @@ -18,7 +22,7 @@ def get_errno(exc): except IndexError: return None -if sys.version_info[0]<3: +if sys.version_info[0]<3 and not greenlets.preserves_excinfo: from sys import exc_clear as clear_sys_exc_info else: def clear_sys_exc_info(): diff --git a/eventlet/support/greenlets.py b/eventlet/support/greenlets.py index 6111688..e33f37f 100644 --- a/eventlet/support/greenlets.py +++ b/eventlet/support/greenlets.py @@ -1,7 +1,11 @@ +import distutils.version + try: import greenlet getcurrent = greenlet.greenlet.getcurrent GreenletExit = greenlet.greenlet.GreenletExit + preserves_excinfo = (distutils.version.LooseVersion(greenlet.__version__) + >= distutils.version.LooseVersion('0.3.2')) greenlet = greenlet.greenlet except ImportError, e: raise @@ -9,14 +13,17 @@ except ImportError, e: from py.magic import greenlet getcurrent = greenlet.getcurrent GreenletExit = greenlet.GreenletExit + preserves_excinfo = False except ImportError: try: from stackless import greenlet getcurrent = greenlet.getcurrent GreenletExit = greenlet.GreenletExit + preserves_excinfo = False except ImportError: try: from support.stacklesss import greenlet, getcurrent, GreenletExit + preserves_excinfo = False (greenlet, getcurrent, GreenletExit) # silence pyflakes except ImportError, e: raise ImportError("Unable to find an implementation of greenlet.") diff --git a/tests/hub_test.py b/tests/hub_test.py index 70789e6..6fd1e3b 100644 --- a/tests/hub_test.py +++ b/tests/hub_test.py @@ -1,10 +1,14 @@ from __future__ import with_statement +import sys -from tests import LimitedTestCase, main, skip_with_pyevent, skip_if_no_itimer +from tests import LimitedTestCase, main, skip_with_pyevent, skip_if_no_itimer, skip_unless import time import eventlet from eventlet import hubs from eventlet.green import socket +from eventlet.event import Event +from eventlet.semaphore import Semaphore +from eventlet.support import greenlets DELAY = 0.001 def noop(): @@ -132,6 +136,62 @@ class TestExceptionInMainloop(LimitedTestCase): assert delay >= DELAY*0.9, 'sleep returned after %s seconds (was scheduled for %s)' % (delay, DELAY) +class TestExceptionInGreenthread(LimitedTestCase): + @skip_unless(greenlets.preserves_excinfo) + def test_exceptionpreservation(self): + # events for controlling execution order + gt1event = Event() + gt2event = Event() + + def test_gt1(): + try: + raise KeyError() + except KeyError: + gt1event.send('exception') + gt2event.wait() + assert sys.exc_info()[0] is KeyError + gt1event.send('test passed') + + def test_gt2(): + gt1event.wait() + gt1event.reset() + assert sys.exc_info()[0] is None + try: + raise ValueError() + except ValueError: + gt2event.send('exception') + gt1event.wait() + assert sys.exc_info()[0] is ValueError + + g1 = eventlet.spawn(test_gt1) + g2 = eventlet.spawn(test_gt2) + try: + g1.wait() + g2.wait() + finally: + g1.kill() + g2.kill() + + def test_exceptionleaks(self): + # tests expected behaviour with all versions of greenlet + def test_gt(sem): + try: + raise KeyError() + except KeyError: + sem.release() + hubs.get_hub().switch() + + # semaphores for controlling execution order + sem = Semaphore() + sem.acquire() + g = eventlet.spawn(test_gt, sem) + try: + sem.acquire() + assert sys.exc_info()[0] is None + finally: + g.kill() + + class TestHubSelection(LimitedTestCase): def test_explicit_hub(self): if getattr(hubs.get_hub(), 'uses_twisted_reactor', None):