.. _startup_shutdown:

Startup and Shutdown
====================

The `ASGI lifespan specification`_ includes the ability for awaiting
coroutines before the first byte is received and after the final byte
is sent, through the ``startup`` and ``shutdown`` lifespan events.
This is particularly useful for creating and destroying connection
pools.  Quart supports this via the decorators
:func:`~quart.app.Quart.before_serving` and
:func:`~quart.app.Quart.after_serving`, which function like
:func:`~quart.app.Quart.before_first_request`.

.. _ASGI lifespan specification: https://github.com/django/asgiref/blob/master/specs/lifespan.rst

The decorated functions are all called within the app context,
allowing ``current_app`` and ``g`` to be used.

.. warning::

    Use ``g`` with caution, as it will reset after all the
    ``before_serving`` functions complete. It can still be used within 
    this context. If you want to create something used in routes, try
    storing it on the app instead.

To use this functionality simply do the following:

.. code-block:: python

    @app.before_serving
    async def create_db_pool():
        app.db_pool = await ...
        g.something = something

    @app.before_serving
    async def use_g():
        g.something.do_something()

    @app.route("/")
    async def index():
        app.db_pool.execute(...)
        # g.something is not available here

    @app.after_serving
    async def create_db_pool():
        await app.db_pool.close()

Testing
-------

Quart's default test client does not wait for the ``startup`` and
``shutdown`` events. This must be manually added when testing a Quart
app using the ``before_serving`` or ``after_serving`` decorators. A
recommended pytest fixture is:

.. code-block:: python

    @pytest.fixture(name="app", scope="function")
    async def _app():
        app = create_app()  # Initialize app
        await app.startup()
        yield app
        await app.shutdown()

The app fixture can then be used as normal.

.. code-block:: python

    @pytest.mark.asyncio
    async def test_index(app):
        test_client = app.test_client()
        await test_client.get("/")
        ...
