Skip to content

blackbull.middleware.utils

blackbull.middleware.utils

Utilities for middleware authors.

Public API: - as_middleware: decorator that normalises the send callable so inner send wrappers defined by the middleware always receive plain ASGI event dicts, never Response objects. Works on both async middleware functions and middleware classes (decorates __call__).

as_middleware(target)

Decorator that marks an async function or class as BlackBull middleware.

Wraps call_next so any send callable the middleware passes to it is automatically normalised — Response/JSONResponse objects are expanded into ASGI event dicts before reaching the middleware's inner send wrapper. The wrapper therefore only ever sees plain dict events and does not need isinstance guards.

Applied to an async function (signature (scope, receive, send, call_next))::

@as_middleware
async def timing_mw(scope, receive, send, call_next):
    async def timed_send(event):
        # event is always a dict here
        await send(event)
    await call_next(scope, receive, timed_send)

Applied to a class whose __call__ is the middleware coroutine::

@as_middleware
class Cache:
    async def __call__(self, scope, receive, send, call_next):
        async def cap_send(event):
            # event is always a dict here
            ...
        await call_next(scope, receive, cap_send)

Power users who need to handle raw send arguments (e.g. because their middleware is used in a context where no simplified handlers are registered) should omit this decorator — their call_next is then wired directly to the next handler with no extra wrapping.