Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lifespan documentation #1759

Merged
merged 1 commit into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions connexion/middleware/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,4 @@ def common_error_handler(_request: StarletteRequest, exc: Exception) -> Response
)

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
# Needs to be set so starlette router throws exceptions instead of returning error responses
scope["app"] = "connexion"
await super().__call__(scope, receive, send)
3 changes: 3 additions & 0 deletions connexion/middleware/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,4 +471,7 @@ def run(self, import_string: str = None, **kwargs):
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if self.middleware_stack is None:
self.app, self.middleware_stack = self._build_middleware_stack()
# Set so starlette router throws exceptions instead of returning error responses
# This instance is also passed to any lifespan handler
scope["app"] = self
await self.app(scope, receive, send)
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Documentation
validation
security
context
lifespan
cookbook
exceptions
cli
Expand Down
109 changes: 109 additions & 0 deletions docs/lifespan.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
Lifespan
========

You can register lifespan handlers to run code before the app starts, or after it shuts down.
This ideal for setting up and tearing down database connections or machine learning models for
instance.

.. tab-set::

.. tab-item:: AsyncApp
:sync: AsyncApp

.. code-block:: python

import contextlib
import typing

from connexion import AsyncApp, ConnexionMiddleware, request

@contextlib.asynccontextmanager
def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
"""Called at startup and shutdown, can yield state which will be available on the
request."""
client = Client()
yield {"client": client}
client.close()

def route():
"""Endpoint function called when receiving a request, you can access the state
on the request here."""
client = request.state.client
client.call()

app = AsyncApp(__name__, lifespan=lifespan_handler)

.. tab-item:: FlaskApp
:sync: FlaskApp

.. code-block:: python

import contextlib
import typing

from connexion import FlaskApp, ConnexionMiddleware, request

@contextlib.asynccontextmanager
def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
"""Called at startup and shutdown, can yield state which will be available on the
request."""
client = Client()
yield {"client": client}
client.close()

def route():
"""Endpoint function called when receiving a request, you can access the state
on the request here."""
client = request.state.client
client.call()

app = FlaskApp(__name__, lifespan=lifespan_handler)

.. tab-item:: ConnexionMiddleware
:sync: ConnexionMiddleware

.. code-block:: python

import contextlib
import typing

from asgi_framework import App
from connexion import ConnexionMiddleware, request

@contextlib.asynccontextmanager
def lifespan_handler(app: ConnexionMiddleware) -> typing.AsyncIterator:
"""Called at startup and shutdown, can yield state which will be available on the
request."""
client = Client()
yield {"client": client}
client.close()

def endpoint():
"""Endpoint function called when receiving a request, you can access the state
on the request here."""
client = request.state.client
client.call()

app = App(__name__)
app = ConnexionMiddleware(app, lifespan=lifespan_handler)

Running lifespan in tests
-------------------------

If you want lifespan handlers to be called during tests, you can use the ``test_client`` as a
context manager.

.. code-block:: python

def test_homepage():
app = ... # Set up app
with app.test_client() as client:
# Application's lifespan is called on entering the block.
response = client.get("/")
assert response.status_code == 200

# And the lifespan's teardown is run when exiting the block.

For more information, please refer to the `Starlette documentation`_.

.. _Starlette documentation: https://www.starlette.io/lifespan/
Loading