Search code examples
pythonpython-asyncioquart

Asynchronous Python server: fire and forget at startup


I am writing a server that must handle asynchronous tasks. I'd rather stick to asyncio for the asynchronous code, so I chose to use Quart[-OpenAPI] framework with Uvicorn.

Now, I need to run a task (master.resume() in the code below) when the server is starting up without waiting for it to finish, that is, firing and forgetting it.

I'm not sure if it's even possible with asyncio, as I cannot await for this task but if I don't I get a coroutine X was never awaited error. Using loop.run_until_complete() as suggested in this answer would block the server until the task completes.

Here's a skeleton of the code that I have:

import asyncio
from quart_openapi import Pint, Resource


app = Pint(__name__, title="Test")


class Master:
    ...

    async def resume():
        await coro()

    async def handle_get()
        ...


@app.route("/test")
class TestResource(Resource):
    async def get(self):
        print("Received get")
        asyncio.create_task(master.handle_get())

        return {"message": "Request received"}


master = Master()

# How do I fire & forget master.resume()?
master.resume()  # <== This throws "RuntimeWarning: coroutine 'Master.resume' was never awaited"
asyncio.get_event_loop().run_until_complete(master.resume())  # <== This prevents the server from starting

Should this not be achievable with asyncio/Quart, what would be the proper way to do it?


Solution

  • It is see these docs, in summary,

    @app.before_serving
    async def startup():
        asyncio.ensure_future(master.resume())
    

    I'd hold on to the task though, so that you can cancel it at shutdown,

    @app.before_serving
    async def startup():
        app.background_task = asyncio.ensure_future(master.resume())
    
    @app.after_serving
    async def shutdown():
        app.background_task.cancel()  # Or something similar