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 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.

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:

import gevent.monkey
gevent.monkey.patch_all()

When deploying in production, use Gunicorn or uWSGI with a gevent worker, as described on those pages.

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

@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 request or other Flask context globals within the spawned function, decorate the function with stream_with_context() or 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.

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 flask.Flask.async_to_sync() to run async functions inside gevent.

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.

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

import gevent
gevent.config.loop = "libuv"

import gevent.monkey
gevent.monkey.patch_all()