Skip to content

WSGI request scope

The framework-free middleware the Flask integration specialises. See the request and response integrations for which module to install.

RequestScope

WSGI middleware that opens one depin scope around every request.

Implemented directly against the WSGI protocol rather than a framework's own middleware base class, so it wraps the whole application — the framework's own error handling and teardown callbacks included. Every WSGI integration depin ships specialises this class, supplying only the seed that places its own framework's request object into the frame.

The container is published to the request's context for the duration of the scope, so depin.hosted_container() reaches it from anywhere inside the request. The publication is undone once the scope's teardowns have drained — including when the application returns by raising.

That scope ends when the application returns, not when the response is finished. WSGI hands the server an iterable that the server consumes after the application has returned, and it offers no hook that outlives that return, so a streaming body cannot resolve: by the time the server pulls the first chunk the scope has drained and the container is no longer published. Resolve everything a streaming response needs before returning the iterable, and close over the values. The alternative — materialising the body into a list before returning it — would buy streaming safety by buffering every response in memory, which is a worse default than the boundary being stated, and it would defeat the one thing WSGI streaming is for. ASGI has no such limit; depin.ext.asgi.RequestScope keeps the scope open for the whole response.

Parameters:

Name Type Description Default
app WSGIApp[EnvironT, StartResponseT]

The downstream WSGI application this middleware wraps.

required
container FrozenContainer

The frozen container to host for the duration of each request. Keyword-or-positional because the framework helpers that install middleware pass app positionally and container by keyword.

required
seed ScopeSeeder[EnvironT] | None

Called once per request, before the downstream application runs, to produce the key and value to place into the fresh scope frame. Returning None seeds nothing. Omitting it seeds nothing either.

None

Raises:

Type Description
TeardownError

An async provider left a teardown in the request's synchronous scope. A WSGI application cannot await, so an async provider has no place in one.

ExceptionGroup

One or more teardowns failed when the request's scope closed. Every failure is included; one does not hide another.

Example

Install the middleware once, per the wrapping framework's own idiom::

from depin import ScopeSeed

app.wsgi_app = RequestScope(app.wsgi_app, di, seed=lambda environ: ScopeSeed(Request, Request(environ)))

WSGIApp

Bases: Protocol

Any WSGI application or middleware: the downstream peer RequestScope wraps.

Environ

Environ = MutableMapping[str, object]

The per-request environment: CGI variables plus the wsgi.* server keys.

Mutable, unlike depin.ext.asgi.ASGIScope. That alias is read-only because a framework may spell its connection scope as a TypedDict, which is not a collections.abc.MutableMapping; WSGI has no such case — the specification calls the environment a dictionary, and wsgiref.types.WSGIEnvironment is dict[str, Any] — and middlewares are expected to write into it.

StartResponse

Bases: Protocol

The callable a WSGI application invokes to begin the response.