Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions docs/async-await.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ method in views that inherit from the :class:`flask.views.View` class, as
well as all the HTTP method handlers in views that inherit from the
:class:`flask.views.MethodView` class.

.. admonition:: Using ``async`` with greenlet

When using gevent or eventlet to serve an application or patch the
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.


Performance
-----------
Expand Down Expand Up @@ -78,15 +72,15 @@ Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
handle many concurrent requests, long running requests, and websockets
without requiring multiple worker processes or threads.

It has also already been possible to run Flask with Gevent or Eventlet
to get many of the benefits of async request handling. These libraries
patch low-level Python functions to accomplish this, whereas ``async``/
``await`` and ASGI use standard, modern Python capabilities. Deciding
whether you should use Flask, Quart, or something else is ultimately up
to understanding the specific needs of your project.
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
get many of the benefits of async request handling. Gevent patches low-level
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
standard, modern Python capabilities. Deciding whether you should use gevent
with Flask, or Quart, or something else is ultimately up to understanding the
specific needs of your project.

.. _Quart: https://github.com/pallets/quart
.. _ASGI: https://asgi.readthedocs.io/en/latest/
.. _Quart: https://quart.palletsprojects.com
.. _ASGI: https://asgi.readthedocs.io


Extensions
Expand Down Expand Up @@ -120,6 +114,6 @@ implemented async support, or make a feature request or PR to them.
Other event loops
-----------------

At the moment Flask only supports :mod:`asyncio`. It's possible to
override :meth:`flask.Flask.ensure_sync` to change how async functions
are wrapped to use a different library.
At the moment Flask only supports :mod:`asyncio`. It's possible to override
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
a different library. See :ref:`gevent-asyncio` for an example.
80 changes: 4 additions & 76 deletions docs/deploying/eventlet.rst
Original file line number Diff line number Diff line change
@@ -1,80 +1,8 @@
:orphan:

eventlet
========

Prefer using :doc:`gunicorn` with eventlet workers rather than using
`eventlet`_ directly. Gunicorn provides a much more configurable and
production-tested server.

`eventlet`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.

:doc:`gevent` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.

eventlet provides a WSGI server that can handle many connections at once
instead of one per worker process. You must actually use eventlet in
your own code to see any benefit to using the server.

.. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/


Installing
----------

When using eventlet, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.

Create a virtualenv, install your application, then install
``eventlet``.

.. code-block:: text

$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install eventlet


Running
-------

To use eventlet to serve your application, write a script that imports
its ``wsgi.server``, as well as your app or app factory.

.. code-block:: python
:caption: ``wsgi.py``

import eventlet
from eventlet import wsgi
from hello import create_app

app = create_app()
wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)

.. code-block:: text

$ python wsgi.py
(x) wsgi starting up on http://127.0.0.1:8000


Binding Externally
------------------

eventlet should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of eventlet.

You can bind to all external IPs on a non-privileged port by using
``0.0.0.0`` in the server arguments shown in the previous section.
Don't do this when using a reverse proxy setup, otherwise it will be
possible to bypass the proxy.
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.

``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.
__ https://eventlet.readthedocs.io
14 changes: 5 additions & 9 deletions docs/deploying/gevent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ configurable and production-tested servers.

`gevent`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.

:doc:`eventlet` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.
switching without writing ``async/await`` or using ``asyncio``. This is
not the same as Python's ``async/await``, or the ASGI server spec.

gevent provides a WSGI server that can handle many connections at once
instead of one per worker process. You must actually use gevent in your
own code to see any benefit to using the server.
instead of one per worker process. See :doc:`/gevent` for more
information about enabling it in your application.

.. _gevent: https://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
Expand All @@ -24,8 +21,7 @@ own code to see any benefit to using the server.
Installing
----------

When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
When using gevent, greenlet>=1.0 is required. When using PyPy,
PyPy>=7.3.7 is required.

Create a virtualenv, install your application, then install ``gevent``.
Expand Down
36 changes: 11 additions & 25 deletions docs/deploying/gunicorn.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ multiple worker implementations for performance tuning.
* It does not support Windows (but does run on WSL).
* It is easy to install as it does not require additional dependencies
or compilation.
* It has built-in async worker support using gevent or eventlet.
* It has built-in async worker support using gevent.

This page outlines the basics of running Gunicorn. Be sure to read its
`documentation`_ and use ``gunicorn --help`` to understand what features
Expand Down Expand Up @@ -93,20 +93,19 @@ otherwise it will be possible to bypass the proxy.
IP address in your browser.


Async with gevent or eventlet
-----------------------------
Async with gevent
-----------------

The default sync worker is appropriate for many use cases. If you need
asynchronous support, Gunicorn provides workers using either `gevent`_
or `eventlet`_. This is not the same as Python's ``async/await``, or the
ASGI server spec. You must actually use gevent/eventlet in your own code
to see any benefit to using the workers.
The default sync worker is appropriate for most use cases. If you need numerous,
long running, concurrent connections, Gunicorn provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.

When using either gevent or eventlet, greenlet>=1.0 is required,
otherwise context locals such as ``request`` will not work as expected.
When using PyPy, PyPy>=7.3.7 is required.
.. _gevent: https://www.gevent.org/

To use gevent:
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.

.. code-block:: text

Expand All @@ -115,16 +114,3 @@ To use gevent:
Listening at: http://127.0.0.1:8000 (x)
Using worker: gevent
Booting worker with pid: x

To use eventlet:

.. code-block:: text

$ gunicorn -k eventlet 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: eventlet
Booting worker with pid: x

.. _gevent: https://www.gevent.org/
.. _eventlet: https://eventlet.net/
1 change: 0 additions & 1 deletion docs/deploying/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ discusses platforms that can manage this for you.
mod_wsgi
uwsgi
gevent
eventlet
asgi

WSGI servers have HTTP servers built-in. However, a dedicated HTTP
Expand Down
20 changes: 9 additions & 11 deletions docs/deploying/uwsgi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,16 @@ IP address in your browser.
Async with gevent
-----------------

The default sync worker is appropriate for many use cases. If you need
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
same as Python's ``async/await``, or the ASGI server spec. You must
actually use gevent in your own code to see any benefit to using the
worker.
The default sync worker is appropriate for most use cases. If you need numerous,
long running, concurrent connections, uWSGI provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.

When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
.. _gevent: https://www.gevent.org/

When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.

.. code-block:: text

Expand All @@ -140,6 +141,3 @@ PyPy>=7.3.7 is required.
spawned uWSGI worker 1 (pid: x, cores: 100)
spawned uWSGI http 1 (pid: x)
*** running gevent loop engine [addr:x] ***


.. _gevent: https://www.gevent.org/
125 changes: 125 additions & 0 deletions docs/gevent.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
Async with Gevent
=================

`Gevent`_ patches Python's standard library to run within special async workers
called `greenlets`_. Gevent has existed since long before Python's native
asyncio was available, and Flask has always worked with it.

.. _gevent: https://www.gevent.org
.. _greenlets: https://greenlet.readthedocs.io

Gevent is a reliable way to handle numerous, long lived, concurrent connections,
and to achieve similar capabilities to ASGI and asyncio. This works without
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
greenlet's low level manipulation of the Python interpreter.

Deciding whether you should use gevent with Flask, or `Quart`_, or something
else, is ultimately up to understanding the specific needs of your project.

.. _quart: https://quart.palletsprojects.com


Enabling gevent
---------------

You need to apply gevent's patching as early as possible in your code. This
enables gevent's underlying event loop and converts many Python internals to run
inside it. Add the following at the top of your project's module or top
``__init__.py``:

.. code-block:: python

import gevent.monkey
gevent.monkey.patch_all()

When deploying in production, use :doc:`/deploying/gunicorn` or
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.

To run concurrent tasks within your own code, such as views, use
|gevent.spawn|_:

.. |gevent.spawn| replace:: ``gevent.spawn()``
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn

.. code-block:: python

@app.post("/send")
def send_email():
gevent.spawn(email.send, to="[email protected]", text="example")
return "Email is being sent."

If you need to access :data:`request` or other Flask context globals within the
spawned function, decorate the function with :func:`.stream_with_context` or
:func:`.copy_current_request_context`. Prefer passing the exact data you need
when spawning the function, rather than using the decorators.

.. note::

When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
is required.


.. _gevent-asyncio:

Combining with ``async``/``await``
----------------------------------

Gevent's patching does not interact well with Flask's built-in asyncio support.
If you want to use Gevent and asyncio in the same app, you'll need to override
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.

.. code-block:: python

import gevent.monkey
gevent.monkey.patch_all()

import asyncio
from flask import Flask, request

loop = asyncio.EventLoop()
gevent.spawn(loop.run_forever)

class GeventFlask(Flask):
def async_to_sync(self, func):
def run(*args, **kwargs):
coro = func(*args, **kwargs)
future = asyncio.run_coroutine_threadsafe(coro, loop)
return future.result()

return run

app = GeventFlask(__name__)

@app.get("/")
async def greet():
await asyncio.sleep(1)
return f"Hello, {request.args.get("name", "World")}!"

This starts an asyncio event loop in a gevent worker. Async functions are
scheduled on that event loop. This may still have limitations, and may need to
be modified further when using other asyncio implementations.


libuv
~~~~~

`libuv`_ is another event loop implementation that `gevent supports`_. There's
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
use libuv, use gevent's support, not uvloop. It may be possible to further
modify the ``async_to_sync`` code from the previous section to work with uvloop,
but that's not currently known.

.. _libuv: https://libuv.org/
.. _gevent supports: https://www.gevent.org/loop_impls.html
.. _uvloop: https://uvloop.readthedocs.io/

To enable gevent's libuv support, add the following at the *very* top of your
code, before ``gevent.monkey.patch_all()``:

.. code-block:: python

import gevent
gevent.config.loop = "libuv"

import gevent.monkey
gevent.monkey.patch_all()
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ community-maintained extensions to add even more functionality.
patterns/index
web-security
deploying/index
gevent
async-await


Expand Down
Loading