Skip to content

Commit

Permalink
Make the docs more user friendly (#950)
Browse files Browse the repository at this point in the history
Improve the general index by adding a contribution guide, changelog, etc.

Refactor sections to host various topics and to generate a more
sexy table of context which will be more user friendly.

Refactor formatting heading levels.

Make Asyncio warning message reusable and centralized.

Give more visibility to the Asyncio topic.
  • Loading branch information
4383 committed Apr 2, 2024
1 parent 7eb208b commit c22b896
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 78 deletions.
16 changes: 14 additions & 2 deletions AUTHORS
@@ -1,12 +1,24 @@
Maintainer (i.e., Who To Hassle If You Find Bugs)
-------------------------------------------------
Jakub Stasiak
Nat Goodspeed

The current maintainer(s) are volunteers with unrelated jobs.
We can only pay sporadic attention to responding to your issue and pull request submissions.
Your patience is greatly appreciated!

Active maintainers
~~~~~~~~~~~~~~~~~~

* Itamar Turner-Trauring https://github.com/itamarst
* Tim Burke https://github.com/tipabu
* Hervé Beraud https://github.com/4383

Less active maintainers
~~~~~~~~~~~~~~~~~~~~~~~

* Sergey Shepelev https://github.com/temoto
* Jakub Stasiak https://github.com/jstasiak
* Nat Goodspeed https://github.com/nat-goodspeed

Original Authors
----------------
* Bob Ippolito
Expand Down
29 changes: 29 additions & 0 deletions doc/source/asyncio/asyncio.rst
@@ -0,0 +1,29 @@
.. _asyncio-index:

Asyncio in Eventlet
###################

Asyncio Compatibility
=====================

Compatibility between Asyncio and Eventlet has been recently introduced.

You may be interested by the state of the art of this compatibility and by
the potential limitations, so please take a look at
:ref:`asyncio-compatibility`.

Asyncio Hub & Functions
=======================

Discover the :mod:`Asyncio Hub <eventlet.hubs.asyncio>`

You may also want to take a look to the
:mod:`Asyncio compatibility functions <eventlet.asyncio>`.

Migrating from Eventlet to Asyncio
==================================

Want to use Asyncio and Eventlet together or you simply want to migrate
off of Eventlet?

Follow the :ref:`official migration guide <migration-guide>`.
97 changes: 58 additions & 39 deletions asyncio_compat.md → doc/source/asyncio/compatibility.rst
@@ -1,4 +1,7 @@
# Asyncio compatibility in eventlet
.. _asyncio-compatibility:

Asyncio compatibility in eventlet
#################################

It should be possible to:

Expand All @@ -11,84 +14,100 @@ If this works, it would allow migrating from eventlet to asyncio in a gradual ma
This means migration doesn't have to be done in one stop, neither in libraries nor in the applications that depend on them.
2. Even when an OpenStack library fully migrates to asyncio, it will still be usable by anything that is still running on eventlet.

## Prior art
Prior art
=========

* Gevent has a similar model to eventlet.
There exists an integration between gevent and asyncio that follows model proposed below: https://pypi.org/project/asyncio-gevent/
* Twisted can run on top of the asyncio event loop.
Separately, it includes utilities for mapping its `Deferred` objects (similar to a JavaScript Promise) to the async/await model introduced in newer versions in Python 3, and in the opposite direction it added support for turning async/await functions into `Deferred`s.
In an eventlet context, `GreenThread` would need a similar former of integration to Twisted's `Deferred`.

## Part 1: Implementing asyncio/eventlet interoperability
Part 1: Implementing asyncio/eventlet interoperability
======================================================

There are three different parts involved in integrating eventlet and asyncio for purposes

### 1. Create a hub that runs on asyncio
1. Create a hub that runs on asyncio
------------------------------------

Like many networking frameworks, eventlet has pluggable event loops, in this case called a "hub". Typically hubs wrap system APIs like `select()` and `epoll()`, but there also used to be a hub that ran on Twisted.
Creating a hub that runs on top of the asyncio event loop should be fairly straightforward.

Once this is done, eventlet and asyncio code can run in the same process and the same thread, but they would still have difficulties talking to each other.
This latter requirement requires additional work, as covered by the next two items.

### 2. Calling `async def` functions from eventlet
2. Calling `async def` functions from eventlet
----------------------------------------------

The goal is to allow something like this:

```python
import aiohttp
from eventlet_asyncio import future_to_greenlet # hypothetical API

async def get_url_body(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
.. code::
def eventlet_code():
green_thread = future_to_greenlet(get_url_body("https://example.com"))
return green_thread.wait()
```
import aiohttp
from eventlet_asyncio import future_to_greenlet # hypothetical API
async def get_url_body(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
def eventlet_code():
green_thread = future_to_greenlet(get_url_body("https://example.com"))
return green_thread.wait()
The code would presumably be similar to https://github.com/gfmio/asyncio-gevent/blob/main/asyncio_gevent/future_to_greenlet.py

### 3. Calling eventlet code from asyncio
3. Calling eventlet code from asyncio
-------------------------------------

The goal is to allow something like this:

```python
from urllib.request import urlopen
from eventlet import spawn
from eventlet_asyncio import greenlet_to_future # hypothetical API

def get_url_body(url):
# Looks blocking, but actually isn't
return urlopen(url).read()

# This would likely be common pattern, so could be implemented as decorator...
async def asyncio_code():
greenlet = eventlet.spawn(get_url_body, "https://example.com")
future = greenlet_to_future(greenlet)
return await future
```
.. code::
from urllib.request import urlopen
from eventlet import spawn
from eventlet_asyncio import greenlet_to_future # hypothetical API
def get_url_body(url):
# Looks blocking, but actually isn't
return urlopen(url).read()
# This would likely be common pattern, so could be implemented as decorator...
async def asyncio_code():
greenlet = eventlet.spawn(get_url_body, "https://example.com")
future = greenlet_to_future(greenlet)
return await future
The code would presumably be similar to https://github.com/gfmio/asyncio-gevent/blob/main/asyncio_gevent/future_to_greenlet.py

### 4. Limitations and potential unexpected behavior
4. Limitations and potential unexpected behavior
------------------------------------------------

``concurrent.futures.thread`` just uses normal threads, not Eventlet's special threads.
Similarly, [``asyncio.to_thread()``](https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread) specifically requires regular blocking code, it won't work correctly with Eventlet code.
Similarly, `asyncio.to_thread() <https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread>`_
specifically requires regular blocking code, it won't work correctly with Eventlet code.

Multiple readers are not supported by the Asyncio hub.

## Part 2: How a port would work on a technical level
Part 2: How a port would work on a technical level
==================================================

### Porting a library
Porting a library
=================

1. Usage of eventlet-based APIs would be replaced with usage of asyncio APIs.
For example, `urllib` or `requests` might be replaced with [`aiohttp`](https://docs.aiohttp.org/en/stable/).
For example, `urllib` or `requests` might be replaced with `aiohttp <https://docs.aiohttp.org/en/stable/>`_.
The interoperability above can be used to make sure this continues to work with eventlet-based APIs.

The `awesome-asyncio <https://github.com/timofurrer/awesome-asyncio>`_ github repository propose a curated list of awesome
Python asyncio frameworks, libraries, software and resources. Do not hesitate to take a look at it. You may find
candidates compatible with asyncio that can allow you to replace some of your actual underlying libraries.
2. Over time, APIs would need be migrated to be `async` function, but in the intermediate time frame a standard `def` can still be used, again using the interoperability layer above.
3. Eventually all "blocking" APIs have been removed, at which point everything can be switched to `async def` and `await`, including external API, and the library will no longer depend on eventlet.

### Porting an application
Porting an application
======================

An application would need to install the asyncio hub before kicking off eventlet.
Beyond that porting would be the same as a library.
Expand Down
File renamed without changes.
10 changes: 10 additions & 0 deletions doc/source/asyncio/warning.rst
@@ -0,0 +1,10 @@
.. warning::

Eventlet is now in maintenance mode, so only the changes who are related
to fixing a bug or who are related to the new Asyncio hub will be accepted.
New features outside of the scope of the Asyncio hub won't be accepted.

:ref:`migration-guide` is strongly encouraged. We encourage existing
users to migrate to Asyncio. For further details see the official
:ref:`migration guide <migration-guide>`.

2 changes: 2 additions & 0 deletions doc/source/authors.rst
@@ -1,3 +1,5 @@
.. _authors:

Authors
=======

Expand Down
46 changes: 46 additions & 0 deletions doc/source/contribute.rst
@@ -0,0 +1,46 @@
.. _how-to-contribute:

How to Contribute to Eventlet
#############################

.. include:: asyncio/warning.rst

Contribution are welcome.

You want to report something? Read :ref:`report-a-bug`.

You want to propose changes? Read :ref:`propose-changes`.

.. _report-a-bug:

Report a Bug
=============

You find a bug and you want to report it?

You simply have to `create a new github issue <https://github.com/eventlet/eventlet/issues>`_
where you describe your problem.

Do not forget to provide technical details like:

* the hub you use
* the context of your bug
* the error message you get
* everything else that may help us to understand your problem.

The more you give details, the more we will be able to help you.

.. _propose-changes:

Propose Changes
===============

You may want to propose changes to fix a bug, improve the documentation, etc.

Feel free to open a pull request: https://github.com/eventlet/eventlet/pulls

.. include:: asyncio/warning.rst

We will be happy to review it.

At this point you may be also interested by :ref:`how to test Eventlet <testing-eventlet>`.
2 changes: 2 additions & 0 deletions doc/source/history.rst
@@ -1,3 +1,5 @@
.. _history:

History
-------

Expand Down
3 changes: 2 additions & 1 deletion doc/source/hubs.rst
Expand Up @@ -13,7 +13,8 @@ Eventlet has multiple hub implementations, and when you start using it, it tries
| We discourage new Eventlet projects.
| We encourage existing Eventlet projects to migrate from Eventlet to Asyncio.
| This hub allow you incremental and smooth migration.
| See the `migration guide <https://eventlet.readthedocs.io/en/latest/migration.html>`_ for further details.
| See the :ref:`migration-guide` for further details.
| See the :ref:`asyncio-compatibility` for the current state of the art.
**epolls**
Linux. This is the fastest hub for Linux.
**kqueue**
Expand Down
58 changes: 46 additions & 12 deletions doc/source/index.rst
@@ -1,3 +1,6 @@
Eventlet Documentation
######################

Warning
=======

Expand Down Expand Up @@ -34,8 +37,19 @@ answer them.
.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _open a new issue: https://github.com/eventlet/eventlet/issues/new

Eventlet Documentation
======================
Installation
============

The easiest way to get Eventlet is to use pip::

pip install -U eventlet

To install latest development version once::

pip install -U https://github.com/eventlet/eventlet/archive/master.zip

Usage
=====

Code talks! This is a simple web crawler that fetches a bunch of urls concurrently:

Expand All @@ -57,19 +71,19 @@ Code talks! This is a simple web crawler that fetches a bunch of urls concurren
for body in pool.imap(fetch, urls):
print("got body", len(body))
Supported Python versions
Supported Python Versions
=========================

Currently supporting CPython 3.7+.


Contents
=========
Concepts & References
=====================

.. toctree::
:maxdepth: 2

migration
asyncio/asyncio
basic_usage
design_patterns
patching
Expand All @@ -78,22 +92,42 @@ Contents
threading
zeromq
hubs
testing
environment

modules

process
authors
history
Want to contribute?
===================

.. toctree::
:maxdepth: 2

contribute
testing
maintenance

License
---------
=======
Eventlet is made available under the terms of the open source `MIT license <http://www.opensource.org/licenses/mit-license.php>`_

Changelog
=========

For further details about released versions of Eventlet please take a
look at the :ref:`changelog`.

Authors & History
=================

You have questions or you may have find a bug and you want to contact authors
or maintainers, then please take a look at :ref:`authors`.

You want to learn more about the history of Eventlet, then, please take a
look at :ref:`history`.

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
* :ref:`changelog`

0 comments on commit c22b896

Please sign in to comment.