File: gevent.rst

package info (click to toggle)
flask 3.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 2,668 kB
  • sloc: python: 10,090; makefile: 32; sql: 22; sh: 19
file content (125 lines) | stat: -rw-r--r-- 4,075 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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="example@example.example", 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()