Litestar

Though it is not required, you can use dishka-litestar integration. It features:

  • automatic REQUEST scope management using middleware

  • passing Request object as a context data to providers for HTTP requests

  • injection of dependencies into handler function using decorator.

How to use

  1. Import

from dishka.integrations.litestar import (
    FromDishka,
    LitestarProvider,
    inject,
    setup_dishka,
)
from dishka import make_async_container, Provider, provide, Scope
  1. Create provider. You can use litestar.Request as a factory parameter to access on REQUEST-scope

class YourProvider(Provider):
    @provide(scope=Scope.REQUEST)
    def create_x(self, request: Request) -> X:
         ...
  1. Mark those of your handlers parameters which are to be injected with FromDishka[] and decorate them using @inject

@get('/')
@inject
async def endpoint(
    request: str, gateway: FromDishka[Gateway],
) -> Response:
    ...

3a. (optional) Set route class to each of your litestar routers to enable automatic injection (it works for HTTP and Websockets)

@get('/')
async def endpoint(
    request: str, gateway: FromDishka[Gateway],
) -> Response:
    ...
r = DishkaRouter('', route_handlers=[endpoint])
app = Litestar(route_handlers=[r])
  1. (optional) Use LitestarProvider() when creating container if you are going to use litestar.Request in providers

container = make_async_container(YourProvider(), LitestarProvider())
  1. (optional) Setup lifespan to close container on app termination

@asynccontextmanager
async def lifespan(app: Litestar):
    yield
    await app.state.dishka_container.close()

app = Litestar([endpoint], lifespan=[lifespan])
  1. Setup dishka integration.

setup_dishka(container=container, app=app)

Websockets

For most cases we operate single events like HTTP-requests. In this case we operate only 2 scopes: APP and REQUEST. Websockets are different: for one application you have multiple connections (one per client) and each connection delivers multiple messages. To support this we use additional scope: SESSION:

APPSESSIONREQUEST

In litestar, your view function is called once per event, and there is no way to determine if it is an http or websocket handler. Therefore, you should use the special inject_websocket decorator for websocket handlers. Also decorator can be only used to retrieve SESSION-scoped objects. To achieve REQUEST-scope you can enter in manually:

@websocket_listener("/")
@inject_websocket
async def get_with_request(
    a: FromDishka[A],  # object with Scope.SESSION
    container: FromDishka[AsyncContainer],  # container for Scope.SESSION
    data: dict[str, str]
) -> dict[str, str]:
    # enter the nested scope, which is Scope.REQUEST
    async with container() as request_container:
        b = await request_container.get(B)  # object with Scope.REQUEST
    return {"key": "value"}

or with class-based handler:

class Handler(WebsocketListener):
    path = "/"

    @inject_websocket
    async def on_receive(
        a: FromDishka[A],  # object with Scope.SESSION
        container: FromDishka[AsyncContainer],  # container for Scope.SESSION
        data: dict[str, str]
    ) -> dict[str, str]:
        async with container() as request_container:
            b = await request_container.get(B)  # object with Scope.REQUEST
        return {"key": "value"}