flaskflask-sqlalchemyquart

Access app_context from another task in Quart


My Quart app is created with a create_app factory method.

I also have a third party library incorporated as an additional task via the create_task method.

I pass a callback function to this library which updates my database (via SQLAlchemy). Unfortunately this does not work and raises the exception:

"Attempt to access app outside of a relevant context"

Pushing the app context does not work:

from quart import current_app as app

async with app.app_context():

Looking at Quarts Context Documentation: https://pgjones.gitlab.io/quart/contexts.html it is obvious why, because the app does not exist in the third party task.

Both of these contexts exist per request and allow the global proxies current_app, request, etc… to be resolved. Note that these contexts are task local, and hence will not exist if a task is spawned by ensure_future or create_task.

Does anyone has any other solution to get the app context from another task?

Edit It still won't work. I am using Quart 0.10.0. A more verbose example of my app looks like this:

from app import create_app

from third_party import ThirdParty

third_party = ThirdParty(loop=asyncio.get_event_loop())
app = create_app()

@app.before_serving
async def startup():
    async with app.app_context() as app_context:
        await third_party.start()


@app.after_serving
async def shutdown():
    await third_party.stop()


if __name__ == "__main__":
    app.run()

The ThirdParty is basically this:

class ThirdParty:
    async def start(self):
        self.loop.create_task(self.run())

    async def run(self):
        while True:
            await self.wait_for_trigger()
            # executes my callback function
            await self.custom_callback_func()

my callback function is in another module and I pass to the third_party instance:

from quart import current_app as app

async def custom_callback_func():
    async with app.app_context:
        # update_my database
        # raises "Attempt to access app outside of a relevant context"

If the app_context is automatically copied to created tasks from a task with the app context, why is my example not working?

await third_party.start() inside the with app_context statement calls loop.create_task(run()) which runs my assigned callback function. So why is there no app_context inside this callback available?


Solution

  • I think you must be using a 0.6.X version of Quart? If so the copy_current_app_context (from quart.ctx) can be used to explicitly copy the context into a new task. For example,

    task = asyncio.ensure_future(copy_current_app_context(other_task_function)())
    

    See also this short documentation snippet. Note though that it is written with Quart >= 0.7 which should automatically copy the context between tasks.

    Edit: Following an update to the question.

    I think you are best placed passing the app instance and using it directly, rather than using current_app in the task. This is because there is no app context after before_serving and before the first request. This may change in Quart though, see this issue.