From ae7b7415d81e1d5d8f09637a5c0abb1455fdfe40 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 3 Dec 2014 00:28:13 +0100 Subject: rename the project from aiogreen to aioeventlet --- Makefile | 2 +- README | 25 ++-- aioeventlet.py | 335 +++++++++++++++++++++++++++++++++++++++++++++++ aiogreen.py | 335 ----------------------------------------------- doc/Makefile | 8 +- doc/changelog.rst | 1 + doc/conf.py | 14 +- doc/index.rst | 22 ++-- doc/make.bat | 4 +- doc/openstack.rst | 28 ++-- doc/status.rst | 2 +- doc/using.rst | 78 +++++------ setup.py | 6 +- tests/__init__.py | 4 +- tests/test_add_reader.py | 2 +- tests/test_eventlet.py | 50 +++---- tests/test_greenlet.py | 12 +- 17 files changed, 465 insertions(+), 463 deletions(-) create mode 100644 aioeventlet.py delete mode 100644 aiogreen.py diff --git a/Makefile b/Makefile index f81c0cb..40bccab 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ test: doc: make -C doc html clean: - rm -rf build dist aiogreen.egg-info .tox + rm -rf build dist aioeventlet.egg-info .tox find -name "*.pyc" -delete find -name "__pycache__" -exec rm -rf {} \; make -C doc clean diff --git a/README b/README index 7e71364..a442f82 100644 --- a/README +++ b/README @@ -1,17 +1,18 @@ -aiogreen implements the asyncio API (PEP 3156) on top of eventlet. It makes +aioeventlet implements the asyncio API (PEP 3156) on top of eventlet. It makes 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 ``link_future()`` and -``wrap_greenthread()`` functions. +aioeventlet allows to use greenthreads in asyncio coroutines, and to use +asyncio coroutines, tasks and futures in greenthreads: see ``link_future()`` +and ``wrap_greenthread()`` functions. -The main visible difference between aiogreen and trollius is the behaviour of -``run_forever()``: ``run_forever()`` blocks with trollius, whereas it runs in a -greenthread with aiogreen. It means that aiogreen event loop can run in an -greenthread while the Python main thread runs other greenthreads in parallel. +The main visible difference between aioeventlet and trollius is the behaviour +of ``run_forever()``: ``run_forever()`` blocks with trollius, whereas it runs +in a greenthread with aioeventlet. It means that aioeventlet event loop can run +in an greenthread while the Python main thread runs other greenthreads in +parallel. -* `aiogreen documentation `_ -* `aiogreen project in the Python Cheeseshop (PyPI) - `_ -* `aiogreen project at Bitbucket `_ +* `aioeventlet documentation `_ +* `aioeventlet project in the Python Cheeseshop (PyPI) + `_ +* `aioeventlet project at Bitbucket `_ * Copyright/license: Open source, Apache 2.0. Enjoy! diff --git a/aioeventlet.py b/aioeventlet.py new file mode 100644 index 0000000..6660728 --- /dev/null +++ b/aioeventlet.py @@ -0,0 +1,335 @@ +import eventlet.hubs.hub +import greenlet +import logging +import signal +import sys +socket = eventlet.patcher.original('socket') +threading = eventlet.patcher.original('threading') + +logger = logging.getLogger('aioeventlet') + +try: + import asyncio + + if sys.platform == 'win32': + from asyncio.windows_utils import socketpair + else: + socketpair = socket.socketpair +except ImportError: + import trollius as asyncio + + if sys.platform == 'win32': + from trollius.windows_utils import socketpair + else: + socketpair = socket.socketpair + +if eventlet.patcher.is_monkey_patched('socket'): + # trollius must use call original socket and threading functions. + # Examples: socket.socket(), socket.socketpair(), + # threading.current_thread(). + asyncio.base_events.socket = socket + asyncio.events.threading = threading + if sys.platform == 'win32': + asyncio.windows_events.socket = socket + asyncio.windows_utils.socket = socket + else: + asyncio.unix_events.socket = socket + asyncio.unix_events.threading = threading + # FIXME: patch also trollius.py3_ssl + + # BaseDefaultEventLoopPolicy._Local must inherit from threading.local + # of the original threading module, not the patched threading module + class _Local(threading.local): + _loop = None + _set_called = False + + asyncio.events.BaseDefaultEventLoopPolicy._Local = _Local + +_EVENT_READ = asyncio.selectors.EVENT_READ +_EVENT_WRITE = asyncio.selectors.EVENT_WRITE +_HUB_READ = eventlet.hubs.hub.READ +_HUB_WRITE = eventlet.hubs.hub.WRITE + +# Eventlet 0.15 or newer? +_EVENTLET15 = hasattr(eventlet.hubs.hub.noop, 'mark_as_closed') + + +class _TpoolExecutor(object): + def __init__(self, loop): + import eventlet.tpool + self._loop = loop + self._tpool = eventlet.tpool + + def submit(self, fn, *args, **kwargs): + f = asyncio.Future(loop=self._loop) + try: + res = self._tpool.execute(fn, *args, **kwargs) + except Exception as exc: + f.set_exception(exc) + else: + f.set_result(res) + return f + + def shutdown(self, wait=True): + self._tpool.killall() + + +class _Selector(asyncio.selectors._BaseSelectorImpl): + def __init__(self, loop, hub): + super(_Selector, self).__init__() + # fd => events + self._notified = {} + self._loop = loop + self._hub = hub + # eventlet.event.Event() used by FD notifiers to wake up select() + self._event = None + + def close(self): + keys = list(self.get_map().values()) + for key in keys: + self.unregister(key.fd) + super(_Selector, self).close() + + def _add(self, fd, event): + if event == _EVENT_READ: + event_type = _HUB_READ + func = self._notify_read + else: + event_type = _HUB_WRITE + func = self._notify_write + + if _EVENTLET15: + self._hub.add(event_type, fd, func, self._throwback, None) + else: + self._hub.add(event_type, fd, func) + + def register(self, fileobj, events, data=None): + key = super(_Selector, self).register(fileobj, events, data) + if events & _EVENT_READ: + self._add(key.fd, _EVENT_READ) + if events & _EVENT_WRITE: + self._add(key.fd, _EVENT_WRITE) + return key + + def _remove(self, fd, event): + if event == _EVENT_READ: + event_type = _HUB_READ + else: + event_type = _HUB_WRITE + try: + listener = self._hub.listeners[event_type][fd] + except KeyError: + pass + else: + self._hub.remove(listener) + + def unregister(self, fileobj): + key = super(_Selector, self).unregister(fileobj) + self._remove(key.fd, _EVENT_READ) + self._remove(key.fd, _EVENT_WRITE) + return key + + def _notify(self, fd, event): + if fd in self._notified: + self._notified[fd] |= event + else: + self._notified[fd] = event + if self._event is not None and not self._event.ready(): + # wakeup the select() method + self._event.send("ready") + + def _notify_read(self, fd): + self._notify(fd, _EVENT_READ) + + def _notify_write(self, fd): + self._notify(fd, _EVENT_WRITE) + + def _throwback(self, fd): + # FIXME: do something with the FD in this case? + pass + + def _read_events(self): + notified = self._notified + self._notified = {} + ready = [] + for fd, events in notified.items(): + key = self.get_key(fd) + ready.append((key, events & key.events)) + return ready + + def select(self, timeout): + events = self._read_events() + if events: + return events + + self._event = eventlet.event.Event() + try: + if timeout is not None: + def timeout_cb(event): + if event.ready(): + return + event.send('timeout') + + eventlet.spawn_after(timeout, timeout_cb, self._event) + + self._event.wait() + # FIXME: cancel the timeout_cb if wait() returns 'ready'? + else: + # blocking call + self._event.wait() + return self._read_events() + finally: + self._event = None + + +class EventLoop(asyncio.SelectorEventLoop): + def __init__(self): + self._greenthread = None + + # Store a reference to the hub to ensure + # that we always use the same hub + self._hub = eventlet.hubs.get_hub() + + selector = _Selector(self, self._hub) + + super(EventLoop, self).__init__(selector=selector) + + # Force a call to set_debug() to set hub.debug_blocking + self.set_debug(self.get_debug()) + + if eventlet.patcher.is_monkey_patched('thread'): + self._default_executor = _TpoolExecutor(self) + + def call_soon(self, callback, *args): + handle = super(EventLoop, self).call_soon(callback, *args) + if self._selector is not None and self._selector._event: + # selector.select() is running: write into the self-pipe to wake up + # the selector + self._write_to_self() + return handle + + def call_at(self, when, callback, *args): + handle = super(EventLoop, self).call_at(when, callback, *args) + if self._selector is not None and self._selector._event: + # selector.select() is running: write into the self-pipe to wake up + # the selector + self._write_to_self() + return handle + + def set_debug(self, debug): + super(EventLoop, self).set_debug(debug) + + self._hub.debug_exceptions = debug + + # Detect blocking eventlet functions. The feature is implemented with + # signal.alarm() which is is not available on Windows. Signal handlers + # can only be set from the main loop. So detecting blocking functions + # cannot be used on Windows nor from a thread different than the main + # thread. + self._hub.debug_blocking = ( + debug + and (sys.platform != 'win32') + and isinstance(threading.current_thread(), threading._MainThread)) + + if (self._hub.debug_blocking + and hasattr(self, 'slow_callback_duration')): + self._hub.debug_blocking_resolution = self.slow_callback_duration + + def run_forever(self): + self._greenthread = eventlet.getcurrent() + try: + super(EventLoop, self).run_forever() + finally: + if self._hub.debug_blocking: + # eventlet event loop is still running: cancel the current + # detection of blocking tasks + signal.alarm(0) + self._greenthread = None + + def time(self): + return self._hub.clock() + + +class EventLoopPolicy(asyncio.DefaultEventLoopPolicy): + _loop_factory = EventLoop + + +def wrap_greenthread(gt, loop=None): + """Wrap an eventlet GreenThread, or a greenlet, into a Future object. + + The Future object waits for the completion of a greenthread. The result + or the exception of the greenthread will be stored in the Future object. + + The greenthread must be wrapped before its execution starts. If the + greenthread is running or already finished, an exception is raised. + + For greenlets, the run attribute must be set. + """ + if loop is None: + loop = asyncio.get_event_loop() + fut = asyncio.Future(loop=loop) + + if not isinstance(gt, greenlet.greenlet): + raise TypeError("greenthread or greenlet request, not %s" + % type(gt)) + + if gt: + raise RuntimeError("wrap_greenthread: the greenthread is running") + if gt.dead: + raise RuntimeError("wrap_greenthread: the greenthread already finished") + + if isinstance(gt, eventlet.greenthread.GreenThread): + orig_main = gt.run + def wrap_func(*args, **kw): + try: + orig_main(*args, **kw) + except Exception as exc: + fut.set_exception(exc) + else: + result = gt.wait() + fut.set_result(result) + gt.run = wrap_func + else: + try: + orig_func = gt.run + except AttributeError: + raise RuntimeError("wrap_greenthread: the run attribute " + "of the greenlet is not set") + def wrap_func(*args, **kw): + try: + result = orig_func(*args, **kw) + except Exception as exc: + fut.set_exception(exc) + else: + fut.set_result(result) + gt.run = wrap_func + return fut + + +def yield_future(future, loop=None): + """Wait for a future, a task, or a coroutine object from a greenthread. + + Yield control other eligible eventlet coroutines until the future is done + (finished successfully or failed with an exception). + + Return the result or raise the exception of the future. + + The function must not be called from the greenthread + of the aioeventlet event loop. + """ + future = asyncio.async(future, loop=loop) + if future._loop._greenthread == eventlet.getcurrent(): + raise RuntimeError("yield_future() must not be called from " + "the greenthread of the aioeventlet 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/aiogreen.py b/aiogreen.py deleted file mode 100644 index 31e53eb..0000000 --- a/aiogreen.py +++ /dev/null @@ -1,335 +0,0 @@ -import eventlet.hubs.hub -import greenlet -import logging -import signal -import sys -socket = eventlet.patcher.original('socket') -threading = eventlet.patcher.original('threading') - -logger = logging.getLogger('aiogreen') - -try: - import asyncio - - if sys.platform == 'win32': - from asyncio.windows_utils import socketpair - else: - socketpair = socket.socketpair -except ImportError: - import trollius as asyncio - - if sys.platform == 'win32': - from trollius.windows_utils import socketpair - else: - socketpair = socket.socketpair - -if eventlet.patcher.is_monkey_patched('socket'): - # trollius must use call original socket and threading functions. - # Examples: socket.socket(), socket.socketpair(), - # threading.current_thread(). - asyncio.base_events.socket = socket - asyncio.events.threading = threading - if sys.platform == 'win32': - asyncio.windows_events.socket = socket - asyncio.windows_utils.socket = socket - else: - asyncio.unix_events.socket = socket - asyncio.unix_events.threading = threading - # FIXME: patch also trollius.py3_ssl - - # BaseDefaultEventLoopPolicy._Local must inherit from threading.local - # of the original threading module, not the patched threading module - class _Local(threading.local): - _loop = None - _set_called = False - - asyncio.events.BaseDefaultEventLoopPolicy._Local = _Local - -_EVENT_READ = asyncio.selectors.EVENT_READ -_EVENT_WRITE = asyncio.selectors.EVENT_WRITE -_HUB_READ = eventlet.hubs.hub.READ -_HUB_WRITE = eventlet.hubs.hub.WRITE - -# Eventlet 0.15 or newer? -_EVENTLET15 = hasattr(eventlet.hubs.hub.noop, 'mark_as_closed') - - -class _TpoolExecutor(object): - def __init__(self, loop): - import eventlet.tpool - self._loop = loop - self._tpool = eventlet.tpool - - def submit(self, fn, *args, **kwargs): - f = asyncio.Future(loop=self._loop) - try: - res = self._tpool.execute(fn, *args, **kwargs) - except Exception as exc: - f.set_exception(exc) - else: - f.set_result(res) - return f - - def shutdown(self, wait=True): - self._tpool.killall() - - -class _Selector(asyncio.selectors._BaseSelectorImpl): - def __init__(self, loop, hub): - super(_Selector, self).__init__() - # fd => events - self._notified = {} - self._loop = loop - self._hub = hub - # eventlet.event.Event() used by FD notifiers to wake up select() - self._event = None - - def close(self): - keys = list(self.get_map().values()) - for key in keys: - self.unregister(key.fd) - super(_Selector, self).close() - - def _add(self, fd, event): - if event == _EVENT_READ: - event_type = _HUB_READ - func = self._notify_read - else: - event_type = _HUB_WRITE - func = self._notify_write - - if _EVENTLET15: - self._hub.add(event_type, fd, func, self._throwback, None) - else: - self._hub.add(event_type, fd, func) - - def register(self, fileobj, events, data=None): - key = super(_Selector, self).register(fileobj, events, data) - if events & _EVENT_READ: - self._add(key.fd, _EVENT_READ) - if events & _EVENT_WRITE: - self._add(key.fd, _EVENT_WRITE) - return key - - def _remove(self, fd, event): - if event == _EVENT_READ: - event_type = _HUB_READ - else: - event_type = _HUB_WRITE - try: - listener = self._hub.listeners[event_type][fd] - except KeyError: - pass - else: - self._hub.remove(listener) - - def unregister(self, fileobj): - key = super(_Selector, self).unregister(fileobj) - self._remove(key.fd, _EVENT_READ) - self._remove(key.fd, _EVENT_WRITE) - return key - - def _notify(self, fd, event): - if fd in self._notified: - self._notified[fd] |= event - else: - self._notified[fd] = event - if self._event is not None and not self._event.ready(): - # wakeup the select() method - self._event.send("ready") - - def _notify_read(self, fd): - self._notify(fd, _EVENT_READ) - - def _notify_write(self, fd): - self._notify(fd, _EVENT_WRITE) - - def _throwback(self, fd): - # FIXME: do something with the FD in this case? - pass - - def _read_events(self): - notified = self._notified - self._notified = {} - ready = [] - for fd, events in notified.items(): - key = self.get_key(fd) - ready.append((key, events & key.events)) - return ready - - def select(self, timeout): - events = self._read_events() - if events: - return events - - self._event = eventlet.event.Event() - try: - if timeout is not None: - def timeout_cb(event): - if event.ready(): - return - event.send('timeout') - - eventlet.spawn_after(timeout, timeout_cb, self._event) - - self._event.wait() - # FIXME: cancel the timeout_cb if wait() returns 'ready'? - else: - # blocking call - self._event.wait() - return self._read_events() - finally: - self._event = None - - -class EventLoop(asyncio.SelectorEventLoop): - def __init__(self): - self._greenthread = None - - # Store a reference to the hub to ensure - # that we always use the same hub - self._hub = eventlet.hubs.get_hub() - - selector = _Selector(self, self._hub) - - super(EventLoop, self).__init__(selector=selector) - - # Force a call to set_debug() to set hub.debug_blocking - self.set_debug(self.get_debug()) - - if eventlet.patcher.is_monkey_patched('thread'): - self._default_executor = _TpoolExecutor(self) - - def call_soon(self, callback, *args): - handle = super(EventLoop, self).call_soon(callback, *args) - if self._selector is not None and self._selector._event: - # selector.select() is running: write into the self-pipe to wake up - # the selector - self._write_to_self() - return handle - - def call_at(self, when, callback, *args): - handle = super(EventLoop, self).call_at(when, callback, *args) - if self._selector is not None and self._selector._event: - # selector.select() is running: write into the self-pipe to wake up - # the selector - self._write_to_self() - return handle - - def set_debug(self, debug): - super(EventLoop, self).set_debug(debug) - - self._hub.debug_exceptions = debug - - # Detect blocking eventlet functions. The feature is implemented with - # signal.alarm() which is is not available on Windows. Signal handlers - # can only be set from the main loop. So detecting blocking functions - # cannot be used on Windows nor from a thread different than the main - # thread. - self._hub.debug_blocking = ( - debug - and (sys.platform != 'win32') - and isinstance(threading.current_thread(), threading._MainThread)) - - if (self._hub.debug_blocking - and hasattr(self, 'slow_callback_duration')): - self._hub.debug_blocking_resolution = self.slow_callback_duration - - def run_forever(self): - self._greenthread = eventlet.getcurrent() - try: - super(EventLoop, self).run_forever() - finally: - if self._hub.debug_blocking: - # eventlet event loop is still running: cancel the current - # detection of blocking tasks - signal.alarm(0) - self._greenthread = None - - def time(self): - return self._hub.clock() - - -class EventLoopPolicy(asyncio.DefaultEventLoopPolicy): - _loop_factory = EventLoop - - -def wrap_greenthread(gt, loop=None): - """Wrap an eventlet GreenThread, or a greenlet, into a Future object. - - The Future object waits for the completion of a greenthread. The result - or the exception of the greenthread will be stored in the Future object. - - The greenthread must be wrapped before its execution starts. If the - greenthread is running or already finished, an exception is raised. - - For greenlets, the run attribute must be set. - """ - if loop is None: - loop = asyncio.get_event_loop() - fut = asyncio.Future(loop=loop) - - if not isinstance(gt, greenlet.greenlet): - raise TypeError("greenthread or greenlet request, not %s" - % type(gt)) - - if gt: - raise RuntimeError("wrap_greenthread: the greenthread is running") - if gt.dead: - raise RuntimeError("wrap_greenthread: the greenthread already finished") - - if isinstance(gt, eventlet.greenthread.GreenThread): - orig_main = gt.run - def wrap_func(*args, **kw): - try: - orig_main(*args, **kw) - except Exception as exc: - fut.set_exception(exc) - else: - result = gt.wait() - fut.set_result(result) - gt.run = wrap_func - else: - try: - orig_func = gt.run - except AttributeError: - raise RuntimeError("wrap_greenthread: the run attribute " - "of the greenlet is not set") - def wrap_func(*args, **kw): - try: - result = orig_func(*args, **kw) - except Exception as exc: - fut.set_exception(exc) - else: - fut.set_result(result) - gt.run = wrap_func - return fut - - -def yield_future(future, loop=None): - """Wait for a future, a task, or a coroutine object from a greenthread. - - Yield control other eligible eventlet coroutines until the future is done - (finished successfully or failed with an exception). - - 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("yield_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/Makefile b/doc/Makefile index a3d64ee..3cf2ed2 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -85,17 +85,17 @@ qthelp: @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aiogreen.qhcp" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aioeventlet.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aiogreen.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aioeventlet.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/aiogreen" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aiogreen" + @echo "# mkdir -p $$HOME/.local/share/devhelp/aioeventlet" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aioeventlet" @echo "# devhelp" epub: diff --git a/doc/changelog.rst b/doc/changelog.rst index 9c1e98f..6e2183e 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -4,6 +4,7 @@ Changelog Version 0.4 ----------- +* Rename the project from ``aiogreen`` to ``aioeventlet`` * Rename the ``link_future()`` function to :func:`yield_future` 2014-10-23: version 0.3 diff --git a/doc/conf.py b/doc/conf.py index a5bf6dc..dd0aa06 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# aiogreen documentation build configuration file, created by +# aioeventlet documentation build configuration file, created by # sphinx-quickstart on Fri Nov 21 04:29:49 2014. # # This file is execfile()d with the current directory set to its @@ -43,7 +43,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'aiogreen' +project = u'aioeventlet' copyright = u'2014, Victor Stinner' # The version info for the project you're documenting, acts as replacement for @@ -176,7 +176,7 @@ html_static_path = ['_static'] #html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'aiogreendoc' +htmlhelp_basename = 'aioeventletdoc' # -- Options for LaTeX output --------------------------------------------- @@ -196,7 +196,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'aiogreen.tex', u'aiogreen Documentation', + ('index', 'aioeventlet.tex', u'aioeventlet Documentation', u'Victor Stinner', 'manual'), ] @@ -226,7 +226,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'aiogreen', u'aiogreen Documentation', + ('index', 'aioeventlet', u'aioeventlet Documentation', [u'Victor Stinner'], 1) ] @@ -240,8 +240,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'aiogreen', u'aiogreen Documentation', - u'Victor Stinner', 'aiogreen', 'One line description of project.', + ('index', 'aioeventlet', u'aioeventlet Documentation', + u'Victor Stinner', 'aioeventlet', 'One line description of project.', 'Miscellaneous'), ] diff --git a/doc/index.rst b/doc/index.rst index 50037d8..6031ade 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,28 +1,28 @@ -aiogreen -======== +aioeventlet +=========== .. image:: poplar_hawk-moth.jpg :alt: Poplar Hawk-moth (Laothoe populi), photo taken in France :align: right :target: https://www.flickr.com/photos/haypo/7181768969/in/set-72157629731066236 -aiogreen implements the asyncio API (`PEP 3156 +aioeventlet implements the asyncio API (`PEP 3156 `_) on top of eventlet. It makes possible to write asyncio code in a project currently written for eventlet. -aiogreen allows to use greenthreads in asyncio coroutines, and to use asyncio +aioeventlet allows to use greenthreads in asyncio coroutines, and to use asyncio coroutines, tasks and futures in greenthreads: see :func:`yield_future` and :func:`wrap_greenthread` functions. -The main visible difference between aiogreen and trollius is the behaviour of +The main visible difference between aioeventlet and trollius is the behaviour of ``run_forever()``: ``run_forever()`` blocks with trollius, whereas it runs in a -greenthread with aiogreen. It means that aiogreen event loop can run in an +greenthread with aioeventlet. It means that aioeventlet event loop can run in an greenthread while the Python main thread runs other greenthreads in parallel. -* `aiogreen documentation `_ -* `aiogreen project in the Python Cheeseshop (PyPI) - `_ -* `aiogreen project at Bitbucket `_ +* `aioeventlet documentation `_ +* `aioeventlet project in the Python Cheeseshop (PyPI) + `_ +* `aioeventlet project at Bitbucket `_ * Copyright/license: Open source, Apache 2.0. Enjoy! Table Of Contents @@ -38,7 +38,7 @@ Table Of Contents Event loops =========== -Projects used by aiogreen: +Projects used by aioeventlet: * `asyncio documentation `_ * `trollius documentation `_ diff --git a/doc/make.bat b/doc/make.bat index ff0c47e..a64366c 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -115,9 +115,9 @@ if "%1" == "qthelp" ( echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\aiogreen.qhcp + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\aioeventlet.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\aiogreen.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\aioeventlet.ghc goto end ) diff --git a/doc/openstack.rst b/doc/openstack.rst index 1199f23..a830f1d 100644 --- a/doc/openstack.rst +++ b/doc/openstack.rst @@ -14,14 +14,14 @@ Already done: * Write the trollius project: port asyncio to Python 2 * Stabilize trollius API * Add trollius dependency to OpenStack -* Write the aiogreen project to provide the asyncio API on top of eventlet +* Write the aioeventlet project to provide the asyncio API on top of eventlet To do: -* Stabilize aiogreen API -* Add aiogreen dependency to OpenStack -* Write an aiogreen executor for Oslo Messaging: rewrite greenio executor - to replace greenio with aiogreen +* Stabilize aioeventlet API +* Add aioeventlet dependency to OpenStack +* Write an aioeventlet executor for Oslo Messaging: rewrite greenio executor + to replace greenio with aioeventlet Second part (to do): rewrite code as trollius coroutines -------------------------------------------------------- @@ -46,9 +46,9 @@ with a lot of legacy code, it has many drivers and the code base is large. To do: * Ceilometer: add trollius dependency and set the trollius event loop policy to - aiogreen -* Ceilometer: change Oslo Messaging executor from "eventlet" to "aiogreen" -* Redesign the service class of Oslo Incubator to support aiogreen and/or + aioeventlet +* Ceilometer: change Oslo Messaging executor from "eventlet" to "aioeventlet" +* Redesign the service class of Oslo Incubator to support aioeventlet and/or trollius. Currently, the class is designed for eventlet. The service class is instanciated before forking, which requires hacks on eventlet to update file descriptors. @@ -77,7 +77,7 @@ Questions: Last part (to do): drop eventlet -------------------------------- -Replace aiogreen event loop with trollius event loop, drop aiogreen and drop +Replace aioeventlet event loop with trollius event loop, drop aioeventlet and drop eventlet at the end. This change will be done on applications one by one. This is no need to port @@ -90,7 +90,7 @@ To do: * Write a "trollius" executor for Oslo Messaging * Ceilometer: Add a blocking call to ``loop.run_forever()`` in the ``main()`` function -* Ceilometer: Replace "aiogreen" executor with "trollius" executor +* Ceilometer: Replace "aioeventlet" executor with "trollius" executor * Ceilometer: Use the standard trollius event loop policy * Ceilometer: drop the eventlet dependency @@ -110,13 +110,13 @@ Optimization, can be done later: History ------- -Maybe the good one, aiogreen project: +Maybe the good one, aioeventlet project: * Novembre 23, two patches posted to Oslo Messaging: - `Add a new aiogreen executor `_ + `Add a new aioeventlet executor `_ and `Add an optional executor callback to dispatcher `_ -* November 19, 2014: First release of the aiogreen project +* November 19, 2014: First release of the aioeventlet project OpenStack Kilo Summit, November 3-7, 2014, at Paris: @@ -164,7 +164,7 @@ First try with a trollius executor for Oslo Messaging: * March 21, 2014: Patch `Replace ad-hoc coroutines with Trollius coroutines `_ proposed to Heat. Heat coroutines are close to Trollius coroutines. Patch abandonned, need to be rewritten, - maybe with aiogreen. + maybe with aioeventlet. * February 20, 2014: The full specification of the blueprint was written: `Oslo/blueprints/asyncio `_ diff --git a/doc/status.rst b/doc/status.rst index c1fc995..6a48c5c 100644 --- a/doc/status.rst +++ b/doc/status.rst @@ -2,7 +2,7 @@ To do ===== * register signals in eventlet hub, only needed for pyevent hub? -* port greenio examples to aiogreen +* port greenio examples to aioeventlet * write unit tests for, and maybe also examples for: - TCP server diff --git a/doc/using.rst b/doc/using.rst index ed82f1a..551d449 100644 --- a/doc/using.rst +++ b/doc/using.rst @@ -1,32 +1,32 @@ Usage ===== -Use aiogreen with trollius --------------------------- +Use aioeventlet with trollius +----------------------------- -aiogreen can be used with trollius, coroutines written with ``yield -From(...)``. Using aiogreen with trollius is a good start to port project +aioeventlet can be used with trollius, coroutines written with ``yield +From(...)``. Using aioeventlet with trollius is a good start to port project written for eventlet to trollius. -To use aiogreen with trollius, set the event loop policy before using an event +To use aioeventlet with trollius, set the event loop policy before using an event loop, example:: - import aiogreen + import aioeventlet import trollius - trollius.set_event_loop_policy(aiogreen.EventLoopPolicy()) + trollius.set_event_loop_policy(aioeventlet.EventLoopPolicy()) # .... Hello World:: - import aiogreen + import aioeventlet import trollius as asyncio def hello_world(): print("Hello World") loop.stop() - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aioeventlet.EventLoopPolicy()) loop = asyncio.get_event_loop() loop.call_soon(hello_world) loop.run_forever() @@ -36,39 +36,39 @@ Hello World:: `Trollius documentation `_. -Use aiogreen with asyncio +Use aioeventlet with asyncio ------------------------- -aiogreen can be used with asyncio, coroutines written with ``yield from ...``. -To use aiogreen with asyncio, set the event loop policy before using an event +aioeventlet can be used with asyncio, coroutines written with ``yield from ...``. +To use aioeventlet with asyncio, set the event loop policy before using an event loop. Example:: - import aiogreen + import aioeventlet import asyncio - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aioeventlet.EventLoopPolicy()) # .... Setting the event loop policy should be enough to examples of the asyncio -documentation with the aiogreen event loop. +documentation with the aioeventlet event loop. .. warning:: - Since aiogreen relies on eventlet, eventlet port to Python 3 is not complete - and asyncio requires Python 3.3 or newer: using aiogreen with asyncio is not - recommended yet. *Using aiogreen with trollius should be preferred right + Since aioeventlet relies on eventlet, eventlet port to Python 3 is not complete + and asyncio requires Python 3.3 or newer: using aioeventlet with asyncio is not + recommended yet. *Using aioeventlet with trollius should be preferred right now*. See the :ref:`status of the eventlet port to Python 3 `. Hello World:: - import aiogreen + import aioeventlet import asyncio def hello_world(): print("Hello World") loop.stop() - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aioeventlet.EventLoopPolicy()) loop = asyncio.get_event_loop() loop.call_soon(hello_world) loop.run_forever() @@ -92,7 +92,7 @@ and threading.Queue to pass data between threads. Use ``threading = eventlet.patcher.original('threading')`` to get the original threading instead of ``import threading``. -It is not possible to run two aiogreen event loops in the same thread. +It is not possible to run two aioeventlet event loops in the same thread. Debug mode @@ -121,10 +121,10 @@ event loop, but it enables less debug checks. API === -aiogreen specific functions: +aioeventlet specific functions: .. warning:: - aiogreen API is not considered as stable yet. + aioeventlet API is not considered as stable yet. yield_future ------------ @@ -135,19 +135,19 @@ yield_future Return the result or raise the exception of the future. - The function must not be called from the greenthread of the aiogreen event + The function must not be called from the greenthread of the aioeventlet event loop. .. versionchanged:: 0.3 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. + aioeventlet event loop. Example of greenthread waiting for a trollius task. The ``progress()`` callback is called regulary to see that the event loop in not blocked:: - import aiogreen + import aioeventlet import eventlet import trollius as asyncio from trollius import From, Return @@ -166,12 +166,12 @@ yield_future task = asyncio.async(coro_slow_sum(1, 2)) - value = aiogreen.yield_future(task) + value = aioeventlet.yield_future(task) print("1 + 2 = %s" % value) loop.stop() - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aioeventlet.EventLoopPolicy()) eventlet.spawn(green_sum) loop = asyncio.get_event_loop() loop.run_forever() @@ -208,7 +208,7 @@ wrap_greenthread Example of trollius coroutine waiting for a greenthread. The ``progress()`` callback is called regulary to see that the event loop in not blocked:: - import aiogreen + import aioeventlet import eventlet import trollius as asyncio from trollius import From, Return @@ -226,12 +226,12 @@ wrap_greenthread loop.call_soon(progress) gt = eventlet.spawn(slow_sum, 1, 2) - fut = aiogreen.wrap_greenthread(gt, loop=loop) + fut = aioeventlet.wrap_greenthread(gt, loop=loop) result = yield From(fut) print("1 + 2 = %s" % result) - asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy()) + asyncio.set_event_loop_policy(aioeventlet.EventLoopPolicy()) loop = asyncio.get_event_loop() loop.run_until_complete(coro_sum()) loop.close() @@ -247,15 +247,15 @@ wrap_greenthread Installation ============ -Install aiogreen with pip -------------------------- +Install aioeventlet with pip +---------------------------- Type:: - pip install aiogreen + pip install aioeventlet -Install aiogreen on Windows with pip ------------------------------------- +Install aioeventlet on Windows with pip +--------------------------------------- Procedure for Python 2.7: @@ -265,13 +265,13 @@ Procedure for Python 2.7: \Python27\python.exe get-pip.py -* Install aiogreen with pip:: +* Install aioeventlet with pip:: - \Python27\python.exe -m pip install aiogreen + \Python27\python.exe -m pip install aioeventlet * pip also installs dependencies: ``eventlet`` and ``trollius`` -Manual installation of aiogreen +Manual installation of aioeventlet ------------------------------- Requirements: diff --git a/setup.py b/setup.py index 0fc1d5b..ca98f10 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ with open("README") as fp: long_description = fp.read() install_options = { - "name": "aiogreen", + "name": "aioeventlet", "version": "0.4", "license": "Apache License 2.0", "author": 'Victor Stinner', @@ -50,7 +50,7 @@ install_options = { "description": "asyncio event loop scheduling callbacks in eventlet.", "long_description": long_description, - "url": "http://aiogreen.readthedocs.org/", + "url": "http://aioeventlet.readthedocs.org/", "classifiers": [ "Programming Language :: Python", @@ -58,7 +58,7 @@ install_options = { "License :: OSI Approved :: Apache Software License", ], - "py_modules": ["aiogreen"], + "py_modules": ["aioeventlet"], #"test_suite": "runtests.runtests", } if SETUPTOOLS: diff --git a/tests/__init__.py b/tests/__init__.py index 2686fbc..7903a95 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,4 +1,4 @@ -import aiogreen +import aioeventlet try: import asyncio except ImportError: @@ -15,7 +15,7 @@ except ImportError: class TestCase(unittest.TestCase): def setUp(self): - policy = aiogreen.EventLoopPolicy() + policy = aioeventlet.EventLoopPolicy() asyncio.set_event_loop_policy(policy) self.addCleanup(asyncio.set_event_loop_policy, None) diff --git a/tests/test_add_reader.py b/tests/test_add_reader.py index 97a65b7..12de90b 100644 --- a/tests/test_add_reader.py +++ b/tests/test_add_reader.py @@ -1,5 +1,5 @@ from __future__ import absolute_import -from aiogreen import socketpair +from aioeventlet import socketpair import eventlet import tests socket = eventlet.patcher.original('socket') diff --git a/tests/test_eventlet.py b/tests/test_eventlet.py index fbb347f..799a676 100644 --- a/tests/test_eventlet.py +++ b/tests/test_eventlet.py @@ -1,4 +1,4 @@ -import aiogreen +import aioeventlet import eventlet import sys import tests @@ -24,16 +24,16 @@ try: result = [] gt = eventlet.spawn(eventlet_slow_append, result, 1, 0.020) - value = yield from aiogreen.wrap_greenthread(gt) + value = yield from aioeventlet.wrap_greenthread(gt) result.append(value) gt = eventlet.spawn(eventlet_slow_append, result, 2, 0.010) - value = yield from aiogreen.wrap_greenthread(gt) + value = yield from aioeventlet.wrap_greenthread(gt) result.append(value) gt = eventlet.spawn(eventlet_slow_error) try: - yield from aiogreen.wrap_greenthread(gt) + yield from aioeventlet.wrap_greenthread(gt) except ValueError as exc: result.append(str(exc)) @@ -60,16 +60,16 @@ except ImportError: result = [] gt = eventlet.spawn(eventlet_slow_append, result, 1, 0.020) - value = yield From(aiogreen.wrap_greenthread(gt)) + value = yield From(aioeventlet.wrap_greenthread(gt)) result.append(value) gt = eventlet.spawn(eventlet_slow_append, result, 2, 0.010) - value = yield From(aiogreen.wrap_greenthread(gt)) + value = yield From(aioeventlet.wrap_greenthread(gt)) result.append(value) gt = eventlet.spawn(eventlet_slow_error) try: - yield From(aiogreen.wrap_greenthread(gt)) + yield From(aioeventlet.wrap_greenthread(gt)) except ValueError as exc: result.append(str(exc)) @@ -90,14 +90,14 @@ except ImportError: def greenthread_yield_future(result, loop): try: - value = aiogreen.yield_future(coro_slow_append(result, 1, 0.020)) + value = aioeventlet.yield_future(coro_slow_append(result, 1, 0.020)) result.append(value) - value = aiogreen.yield_future(coro_slow_append(result, 2, 0.010)) + value = aioeventlet.yield_future(coro_slow_append(result, 2, 0.010)) result.append(value) try: - value = aiogreen.yield_future(coro_slow_error()) + value = aioeventlet.yield_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.yield_future(coro_slow_append(result, 3)) + value = aioeventlet.yield_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.yield_future(fut) + value = aioeventlet.yield_future(fut) result.append(value) self.loop.stop() @@ -206,7 +206,7 @@ class LinkFutureTests(tests.TestCase): def func(fut): try: - value = aiogreen.yield_future(fut) + value = aioeventlet.yield_future(fut) except Exception as exc: result.append('error') else: @@ -221,7 +221,7 @@ class LinkFutureTests(tests.TestCase): def test_yield_future_invalid_type(self): def func(obj): - return aiogreen.yield_future(obj) + return aioeventlet.yield_future(obj) @asyncio.coroutine def coro_func(): @@ -243,7 +243,7 @@ class LinkFutureTests(tests.TestCase): def func(fut): try: - value = aiogreen.yield_future(fut, loop=loop2) + value = aioeventlet.yield_future(fut, loop=loop2) except Exception as exc: result.append(str(exc)) else: @@ -265,7 +265,7 @@ class WrapGreenthreadTests(tests.TestCase): return 'ok' gt = eventlet.spawn(func) - fut = aiogreen.wrap_greenthread(gt) + fut = aioeventlet.wrap_greenthread(gt) result = self.loop.run_until_complete(fut) self.assertEqual(result, 'ok') @@ -278,7 +278,7 @@ class WrapGreenthreadTests(tests.TestCase): # FIXME: the unit test must fail!? with tests.mock.patch('traceback.print_exception') as print_exception: gt = eventlet.spawn(func) - fut = aiogreen.wrap_greenthread(gt) + fut = aioeventlet.wrap_greenthread(gt) self.assertRaises(ValueError, self.loop.run_until_complete, fut) # the exception must not be logger by traceback: the caller must @@ -287,7 +287,7 @@ class WrapGreenthreadTests(tests.TestCase): def test_wrap_greenthread_running(self): def func(): - return aiogreen.wrap_greenthread(gt) + return aioeventlet.wrap_greenthread(gt) self.loop.set_debug(False) gt = eventlet.spawn(func) @@ -304,7 +304,7 @@ class WrapGreenthreadTests(tests.TestCase): msg = "wrap_greenthread: the greenthread already finished" self.assertRaisesRegexp(RuntimeError, msg, - aiogreen.wrap_greenthread, gt) + aioeventlet.wrap_greenthread, gt) def test_coro_wrap_greenthread(self): result = self.loop.run_until_complete(coro_wrap_greenthread()) @@ -313,14 +313,14 @@ class WrapGreenthreadTests(tests.TestCase): def test_wrap_invalid_type(self): def func(): pass - self.assertRaises(TypeError, aiogreen.wrap_greenthread, func) + self.assertRaises(TypeError, aioeventlet.wrap_greenthread, func) @asyncio.coroutine def coro_func(): pass coro_obj = coro_func() self.addCleanup(coro_obj.close) - self.assertRaises(TypeError, aiogreen.wrap_greenthread, coro_obj) + self.assertRaises(TypeError, aioeventlet.wrap_greenthread, coro_obj) class WrapGreenletTests(tests.TestCase): @@ -330,7 +330,7 @@ class WrapGreenletTests(tests.TestCase): return "ok" gt = eventlet.spawn_n(func) - fut = aiogreen.wrap_greenthread(gt) + fut = aioeventlet.wrap_greenthread(gt) result = self.loop.run_until_complete(fut) self.assertEqual(result, "ok") @@ -341,7 +341,7 @@ class WrapGreenletTests(tests.TestCase): raise ValueError(7) gt = eventlet.spawn_n(func) - fut = aiogreen.wrap_greenthread(gt) + fut = aioeventlet.wrap_greenthread(gt) self.assertRaises(ValueError, self.loop.run_until_complete, fut) def test_wrap_greenlet_running(self): @@ -350,7 +350,7 @@ class WrapGreenletTests(tests.TestCase): def func(): try: gt = eventlet.getcurrent() - fut = aiogreen.wrap_greenthread(gt) + fut = aioeventlet.wrap_greenthread(gt) except Exception as exc: event.send_exception(exc) else: @@ -368,7 +368,7 @@ class WrapGreenletTests(tests.TestCase): gt = eventlet.spawn_n(func) event.wait() msg = "wrap_greenthread: the greenthread already finished" - self.assertRaisesRegexp(RuntimeError, msg, aiogreen.wrap_greenthread, gt) + self.assertRaisesRegexp(RuntimeError, msg, aioeventlet.wrap_greenthread, gt) if __name__ == '__main__': diff --git a/tests/test_greenlet.py b/tests/test_greenlet.py index f411a8e..5a4f491 100644 --- a/tests/test_greenlet.py +++ b/tests/test_greenlet.py @@ -1,4 +1,4 @@ -import aiogreen +import aioeventlet import greenlet import tests @@ -9,7 +9,7 @@ class WrapGreenletTests(tests.TestCase): return value * 3 gl = greenlet.greenlet(func) - fut = aiogreen.wrap_greenthread(gl) + fut = aioeventlet.wrap_greenthread(gl) gl.switch(5) result = self.loop.run_until_complete(fut) self.assertEqual(result, 15) @@ -19,7 +19,7 @@ class WrapGreenletTests(tests.TestCase): raise ValueError(7) gl = greenlet.greenlet(func) - fut = aiogreen.wrap_greenthread(gl) + fut = aioeventlet.wrap_greenthread(gl) gl.switch() self.assertRaises(ValueError, self.loop.run_until_complete, fut) @@ -27,12 +27,12 @@ class WrapGreenletTests(tests.TestCase): gl = greenlet.greenlet() msg = "wrap_greenthread: the run attribute of the greenlet is not set" self.assertRaisesRegexp(RuntimeError, msg, - aiogreen.wrap_greenthread, gl) + aioeventlet.wrap_greenthread, gl) def test_wrap_greenlet_running(self): def func(value): gl = greenlet.getcurrent() - return aiogreen.wrap_greenthread(gl) + return aioeventlet.wrap_greenthread(gl) gl = greenlet.greenlet(func) msg = "wrap_greenthread: the greenthread is running" @@ -45,7 +45,7 @@ class WrapGreenletTests(tests.TestCase): gl = greenlet.greenlet(func) gl.switch(5) msg = "wrap_greenthread: the greenthread already finished" - self.assertRaisesRegexp(RuntimeError, msg, aiogreen.wrap_greenthread, gl) + self.assertRaisesRegexp(RuntimeError, msg, aioeventlet.wrap_greenthread, gl) if __name__ == '__main__': -- cgit v1.2.1