diff options
-rw-r--r-- | README | 33 | ||||
-rw-r--r-- | aiogreen.py | 18 | ||||
-rw-r--r-- | tests/test_eventlet.py | 45 | ||||
-rw-r--r-- | tox.ini | 9 |
4 files changed, 86 insertions, 19 deletions
@@ -72,6 +72,38 @@ Hello World:: loop.close() +Use a greenthread in a coroutine +-------------------------------- + +Use the ``wrap_greenthread()`` function to wrap a greenthread into a Future +object. The Future object waits for the completion of a greenthread. + +Example with asyncio:: + + def slow_sum(x, y): + eventlet.sleep(1.0) + return x + y + + @asyncio.coroutine + def coro_sum(): + gt = eventlet.spawn(slow_sum, 1, 2) + fut = aiogreen.wrap_greenthread(gt, loop=loop) + result = yield from fut + return result + +Note: In debug mode, when a greenthread raises an exception, the exception is +logged to sys.stderr by eventlet, even if it is correctly copied to the Future +object. + + +API +=== + +Functions which are not in asyncio: + +* ``wrap_greenthread(gt)``: wrap a greenthread into a Future object + + Installation ============ @@ -139,6 +171,7 @@ only call the callback once per loop iteration. Changes: +* Add the ``wrap_greenthread()`` function to wrap a greenthread into a Future * Support also eventlet 0.14, not only eventlet 0.15 or newer * Support eventlet with monkey-patching * Rewrite the code handling file descriptors to ensure that the listener is diff --git a/aiogreen.py b/aiogreen.py index 5c45635..e7558c3 100644 --- a/aiogreen.py +++ b/aiogreen.py @@ -68,6 +68,24 @@ class _TpoolExecutor(object): self._tpool.killall() +def wrap_greenthread(gt, loop=None): + """Wrap an eventlet greenthread into a Future object.""" + if loop is None: + loop = asyncio.get_event_loop() + fut = asyncio.Future(loop=loop) + + def copy_result(gt): + try: + value = gt.wait() + except Exception as exc: + loop.call_soon(fut.set_exception, exc) + else: + loop.call_soon(fut.set_result, value) + + gt.link(copy_result) + return fut + + class _Selector(asyncio.selectors._BaseSelectorImpl): def __init__(self, loop, hub): super(_Selector, self).__init__() diff --git a/tests/test_eventlet.py b/tests/test_eventlet.py index eb428d7..4ff9e8c 100644 --- a/tests/test_eventlet.py +++ b/tests/test_eventlet.py @@ -1,5 +1,8 @@ +import aiogreen import eventlet import tests +from tests import unittest + def eventlet_slow_append(result, value, delay): eventlet.sleep(delay) @@ -33,22 +36,25 @@ try: return value @asyncio.coroutine - def coro_chain_greenthread(): + def coro_wrap_greenthread(): result = [] loop = asyncio.get_event_loop() g1 = eventlet.spawn(eventlet_slow_append, result, 1, 0.2) - value = yield from wait_greenthread(g1, loop=loop) + fut = aiogreen.wrap_greenthread(g1, loop=loop) + value = yield from fut result.append(value) g2 = eventlet.spawn(eventlet_slow_append, result, 2, 0.1) - value = yield from wait_greenthread(g2, loop=loop) + fut = aiogreen.wrap_greenthread(g2, loop=loop) + value = yield from fut result.append(value) g3 = eventlet.spawn(eventlet_slow_error, 0.001) + fut = aiogreen.wrap_greenthread(g3, loop=loop) try: - yield from wait_greenthread(g3, loop=loop) + yield from fut except ValueError as exc: result.append(str(exc)) @@ -89,22 +95,24 @@ except ImportError: raise Return(value) @asyncio.coroutine - def coro_chain_greenthread(): + def coro_wrap_greenthread(): result = [] loop = asyncio.get_event_loop() g1 = eventlet.spawn(eventlet_slow_append, result, 1, 0.2) - - value = yield From(wait_greenthread(g1, loop=loop)) + fut = aiogreen.wrap_greenthread(g1, loop=loop) + value = yield From(fut) result.append(value) g2 = eventlet.spawn(eventlet_slow_append, result, 2, 0.1) - value = yield From(wait_greenthread(g2, loop=loop)) + fut = aiogreen.wrap_greenthread(g2, loop=loop) + value = yield From(fut) result.append(value) g3 = eventlet.spawn(eventlet_slow_error, 0.001) + fut = aiogreen.wrap_greenthread(g3, loop=loop) try: - yield From(wait_greenthread(g3, loop=loop)) + yield From(fut) except ValueError as exc: result.append(str(exc)) @@ -123,7 +131,7 @@ except ImportError: raise ValueError("error") -def wait_task(task): +def link_task(task): event = eventlet.event.Event() def done(fut): try: @@ -137,19 +145,19 @@ def wait_task(task): task.add_done_callback(done) return event.wait() -def greenthread_chain_coro(result, loop): +def greenthread_link_task(result, loop): try: t1 = asyncio.async(coro_slow_append(result, 1, 0.2), loop=loop) - value = wait_task(t1) + value = link_task(t1) result.append(value) t2 = asyncio.async(coro_slow_append(result, 2, 0.1), loop=loop) - value = wait_task(t2) + value = link_task(t2) result.append(value) t3 = asyncio.async(coro_slow_error(0.001), loop=loop) try: - value = wait_task(t3) + value = link_task(t3) except ValueError as exc: result.append(str(exc)) @@ -204,13 +212,14 @@ class EventletTests(tests.TestCase): self.loop.run_forever() self.assertEqual(result, ["spawn", "spawn_after"]) - def test_coro_chain_greenthread(self): - result = self.loop.run_until_complete(coro_chain_greenthread()) + def test_coro_wrap_greenthread(self): + result = self.loop.run_until_complete(coro_wrap_greenthread()) self.assertEqual(result, [1, 10, 2, 20, 'error', 4]) - def test_greenthread_chain_coro(self): + def test_greenthread_link_task(self): result = [] - self.loop.call_soon(eventlet.spawn, greenthread_chain_coro, result, self.loop) + self.loop.call_soon(eventlet.spawn, + greenthread_link_task, result, self.loop) self.loop.run_forever() self.assertEqual(result, [1, 10, 2, 20, 'error', 4]) @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py27_patch,py32,py33,py34 +envlist = py26,py27,py27_patch,py32,py33,py34,py35 [testenv:py26] setenv = @@ -49,3 +49,10 @@ setenv = deps= eventlet commands=python runtests.py -r + +[testenv:py35] +setenv = + PYTHONASYNCIODEBUG = 1 +deps= + eventlet +commands=python runtests.py -r |