Source code for linkd.ext.quart
# -*- coding: utf-8 -*-
# Copyright (c) 2025-present tandemdude
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""
Extension module adding support for using linkd-based dependency injection with `Quart <https://quart.palletsprojects.com/en/latest/>`_.
See the examples directory for a full working application using this module.
.. note::
Unlike the ``FastAPI`` and ``Starlette`` integrations, the default :meth:`~linkd.solver.inject` method
will work for route handlers when using this extension.
----
"""
from __future__ import annotations
__all__ = ["Contexts", "DiContextMiddleware"]
import typing as t
from linkd import context as _context
from linkd.ext import _common
if t.TYPE_CHECKING:
from collections.abc import Awaitable
from collections.abc import Callable
from hypercorn.typing import ASGIReceiveCallable
from hypercorn.typing import ASGISendCallable
from hypercorn.typing import Scope
from linkd import solver
ASGIApp = Callable[[Scope, ASGIReceiveCallable, ASGISendCallable], Awaitable[None]]
[docs]
@t.final
class Contexts:
"""Collection of the dependency injection context values linkd.ext.quart uses."""
__slots__ = ()
ROOT = _context.Contexts.ROOT
"""The root DI context - ALL other contexts are built with this as the parent."""
REQUEST = _common.REQUEST_CONTEXT
"""DI context used during HTTP request handling."""
[docs]
class DiContextMiddleware:
"""
Middleware class which handles setting up a DI context for each HTTP request.
Args:
app: The ASGI app this middleware will be applied to.
manager: The dependency injection manager to use when entering the DI context.
Example:
.. code-block:: python
import quart
import linkd
manager = linkd.DependencyInjectionManager()
app = quart.Quart(__name__)
app.asgi_app = linkd.ext.quart.DiContextMiddleware(app.asgi_app, manager)
"""
__slots__ = ("app", "manager")
def __init__(self, app: ASGIApp, manager: solver.DependencyInjectionManager) -> None:
self.app: ASGIApp = app
self.manager: solver.DependencyInjectionManager = manager
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
async with self.manager.enter_context(Contexts.ROOT), self.manager.enter_context(Contexts.REQUEST):
await self.app(scope, receive, send)