From fc6513fad282b1dbabec9e5fc4235f8e94af9fe9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 24 Nov 2014 17:31:32 +0100 Subject: convert link_future to a method of EventletEventLoop --- aiogreen.py | 52 +++++++++++------------ doc/index.rst | 5 ++- doc/using.rst | 113 +++++++++++++++++++++++++++++-------------------- tests/test_eventlet.py | 16 +++---- 4 files changed, 105 insertions(+), 81 deletions(-) diff --git a/aiogreen.py b/aiogreen.py index 08c8d46..8912d43 100644 --- a/aiogreen.py +++ b/aiogreen.py @@ -256,6 +256,32 @@ class EventletEventLoop(asyncio.SelectorEventLoop): def time(self): return self._hub.clock() + def link_future(self, future): + """Wait for a future, a task, or a coroutine object from a greenthread. + + Return the result or raise the exception of the future. + + The function must not be called from the greenthread + of the aiogreen event loop. + """ + if self._greenthread == eventlet.getcurrent(): + raise RuntimeError("link_future() must not be called from " + "the greenthread of the aiogreen event loop") + + future = asyncio.async(future, loop=self) + event = eventlet.event.Event() + + def done(fut): + try: + result = fut.result() + except Exception as exc: + event.send_exception(exc) + else: + event.send(result) + + future.add_done_callback(done) + return event.wait() + class EventletEventLoopPolicy(asyncio.DefaultEventLoopPolicy): _loop_factory = EventletEventLoop @@ -311,29 +337,3 @@ def wrap_greenthread(gt, loop=None): fut.set_result(result) gt.run = wrap_func return fut - - -def link_future(future, loop=None): - """Wait for a future, a task, or a coroutine object from a greenthread. - - Return the result or raise the exception of the future. - - The function must not be called from the greenthread - of the aiogreen event loop. - """ - future = asyncio.async(future, loop=loop) - if future._loop._greenthread == eventlet.getcurrent(): - raise RuntimeError("link_future() must not be called from " - "the greenthread of the aiogreen event loop") - - event = eventlet.event.Event() - def done(fut): - try: - result = fut.result() - except Exception as exc: - event.send_exception(exc) - else: - event.send(result) - - future.add_done_callback(done) - return event.wait() diff --git a/doc/index.rst b/doc/index.rst index 280f895..d61e650 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -11,8 +11,9 @@ aiogreen implements the asyncio API (`PEP 3156 possible to write asyncio code in a project currently written for eventlet. aiogreen allows to use greenthreads in asyncio coroutines, and to use asyncio -coroutines, tasks and futures in greenthreads: see :func:`link_future` and -:func:`wrap_greenthread` functions. +coroutines, tasks and futures in greenthreads: see +:meth:`EventletEventLoop.link_future` method and :func:`wrap_greenthread` +function. The main visible difference between aiogreen and trollius is the behaviour of ``run_forever()``: ``run_forever()`` blocks with trollius, whereas it runs in a diff --git a/doc/using.rst b/doc/using.rst index 810af1b..827d2ad 100644 --- a/doc/using.rst +++ b/doc/using.rst @@ -14,7 +14,7 @@ loop, example:: import aiogreen import trollius - trollius.set_event_loop_policy(aiogreen.EventLoopPolicy()) + trollius.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) # .... Hello World:: @@ -26,7 +26,7 @@ Hello World:: print("Hello World") loop.stop() - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) loop = asyncio.get_event_loop() loop.call_soon(hello_world) loop.run_forever() @@ -46,7 +46,7 @@ loop. Example:: import aiogreen import asyncio - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) # .... Setting the event loop policy should be enough to examples of the asyncio @@ -68,7 +68,7 @@ Hello World:: print("Hello World") loop.stop() - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) loop = asyncio.get_event_loop() loop.call_soon(hello_world) loop.run_forever() @@ -126,63 +126,78 @@ aiogreen specific functions: .. warning:: aiogreen API is not considered as stable yet. -link_future ------------ +EventletEventLoop +----------------- -.. function:: link_future(future, loop=None) +.. class:: EventletEventLoop - Wait for a future, a task, or a coroutine object from a greenthread. + asyncio or trollius event loop running on top of eventlet. - Return the result or raise the exception of the future. + The API is the API of asyncio event loop, with one extra method: :meth:`link_future`. - The function must not be called from the greenthread of the aiogreen event - loop. + .. versionchanged:: 0.4 - .. versionchanged:: 0.3 + The class was renamed from ``EventLoop`` to ``EventletEventLoop``. - Coroutine objects are also accepted. Added the *loop* parameter. - An exception is raised if it is called from the greenthread of the - aiogreen event loop. + .. method:: link_future(future) - Example of greenthread waiting for a trollius task. The ``progress()`` - callback is called regulary to see that the event loop in not blocked:: + Wait for a future, a task, or a coroutine object from a greenthread. - import aiogreen - import eventlet - import trollius as asyncio - from trollius import From, Return + Return the result or raise the exception of the future. - def progress(): - print("computation in progress...") - loop.call_later(0.5, progress) + The function must not be called from the greenthread of the aiogreen event + loop. - @asyncio.coroutine - def coro_slow_sum(x, y): - yield From(asyncio.sleep(1.0)) - raise Return(x + y) + .. versionchanged:: 0.4 - def green_sum(): - loop.call_soon(progress) + The function was converted to a method of the + :class:`EventletEventLoop` class. - task = asyncio.async(coro_slow_sum(1, 2)) + .. versionchanged:: 0.3 - value = aiogreen.link_future(task) - print("1 + 2 = %s" % value) + Coroutine objects are also accepted. Added the *loop* parameter. + An exception is raised if it is called from the greenthread of the + aiogreen event loop. - loop.stop() + Example of greenthread waiting for a trollius task. The ``progress()`` + callback is called regulary to see that the event loop in not blocked:: - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) - eventlet.spawn(green_sum) - loop = asyncio.get_event_loop() - loop.run_forever() - loop.close() + import aiogreen + import eventlet + import trollius as asyncio + from trollius import From, Return - Output:: + def progress(): + print("computation in progress...") + loop.call_later(0.5, progress) - computation in progress... - computation in progress... - computation in progress... - 1 + 2 = 3 + @asyncio.coroutine + def coro_slow_sum(x, y): + yield From(asyncio.sleep(1.0)) + raise Return(x + y) + + def green_sum(): + loop.call_soon(progress) + + task = asyncio.async(coro_slow_sum(1, 2)) + + value = loop.link_future(task) + print("1 + 2 = %s" % value) + + loop.stop() + + asyncio.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) + eventlet.spawn(green_sum) + loop = asyncio.get_event_loop() + loop.run_forever() + loop.close() + + Output:: + + computation in progress... + computation in progress... + computation in progress... + 1 + 2 = 3 wrap_greenthread ---------------- @@ -231,7 +246,7 @@ wrap_greenthread result = yield From(fut) print("1 + 2 = %s" % result) - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aiogreen.EventletEventLoopPolicy()) loop = asyncio.get_event_loop() loop.run_until_complete(coro_sum()) loop.close() @@ -244,6 +259,14 @@ wrap_greenthread 1 + 2 = 3 +EventletEventLoopPolicy +----------------------- + +.. class:: EventletEventLoopPolicy + + Event loop policy creating :class:`EventletEventLoop` event loops. + + Installation ============ diff --git a/tests/test_eventlet.py b/tests/test_eventlet.py index 9a83008..c7ee878 100644 --- a/tests/test_eventlet.py +++ b/tests/test_eventlet.py @@ -90,14 +90,14 @@ except ImportError: def greenthread_link_future(result, loop): try: - value = aiogreen.link_future(coro_slow_append(result, 1, 0.020)) + value = loop.link_future(coro_slow_append(result, 1, 0.020)) result.append(value) - value = aiogreen.link_future(coro_slow_append(result, 2, 0.010)) + value = loop.link_future(coro_slow_append(result, 2, 0.010)) result.append(value) try: - value = aiogreen.link_future(coro_slow_error()) + value = loop.link_future(coro_slow_error()) except ValueError as exc: result.append(str(exc)) @@ -174,7 +174,7 @@ class LinkFutureTests(tests.TestCase): result = [] def func(fut): - value = aiogreen.link_future(coro_slow_append(result, 3)) + value = self.loop.link_future(coro_slow_append(result, 3)) result.append(value) self.loop.stop() @@ -188,7 +188,7 @@ class LinkFutureTests(tests.TestCase): def func(event, fut): event.send('link') - value = aiogreen.link_future(fut) + value = self.loop.link_future(fut) result.append(value) self.loop.stop() @@ -206,7 +206,7 @@ class LinkFutureTests(tests.TestCase): def func(fut): try: - value = aiogreen.link_future(fut) + value = self.loop.link_future(fut) except Exception as exc: result.append('error') else: @@ -221,7 +221,7 @@ class LinkFutureTests(tests.TestCase): def test_link_future_invalid_type(self): def func(obj): - return aiogreen.link_future(obj) + return self.loop.link_future(obj) @asyncio.coroutine def coro_func(): @@ -243,7 +243,7 @@ class LinkFutureTests(tests.TestCase): def func(fut): try: - value = aiogreen.link_future(fut, loop=loop2) + value = loop2.link_future(fut) except Exception as exc: result.append(str(exc)) else: -- cgit v1.2.1