How to enable uvicorn to run asynchronously constructed app?

Viewed 4607

Given main.py:

import asyncio

async def new_app():
    # Await some things.

    async def app(scope, receive, send):
        ...

    return app

app = asyncio.run(new_app())

followed by:

uvicorn main.app

gives:

RuntimeError: asyncio.run() cannot be called from a running event loop

This is because uvicorn already started an event loop prior to importing my application. How can I asynchronously construct an application under uvicorn?

1 Answers

You need not use asyncio.run. Your class or function should only implement the ASGI interface. Like so, simplest workable:

# main.py
def app(scope):
    async def asgi(receive, send):
        await send(
            {
                "type": "http.response.start",
                "status": 200,
                "headers": [[b"content-type", b"text/plain"]],
            }
        )
        await send({"type": "http.response.body", "body": b"Hello, world!"})

    return asgi

And you can start it under uvicorn: uvicorn main:app.

Parameter main:app will be parsed imported by uvicorn and executed in such manner within its eventloop:

 app = self.config.loaded_app
 scope: LifespanScope = {
     "type": "lifespan",
     "asgi": {"version": self.config.asgi_version, "spec_version": "2.0"},
 }
 await app(scope, self.receive, self.send)

If you want to make an executable module, you can do this:

import uvicorn
# app definition
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
Related