@inject supports function handlers only. Applied to a method of an aiohttp.web.View subclass it fails on the first request rather than being unsupported:
TypeError: 'View' object is not subscriptable
and the client gets a 500. Reproduced against aiohttp 3.14.3 with a View whose get takes one FromDI parameter, registered with app.router.add_view.
Why
inject returns async def wrapper(request). aiohttp's View._iter calls the bound method with no arguments (ret = await method()), so the wrapper receives only self, and fetch_request_container(self) does self[_CONTAINER_REQUEST_KEY] on the view instance. The request is on self.request; nothing reads it from there.
Prior art
modern-python/modern-di-starlette#28 fixed the same class of bug for Starlette's HTTPEndpoint / WebSocketEndpoint, and its issue (modern-di-starlette#18) named "a second integration in the org needs an injection path for a class-based handler" as the revisit trigger. This is that case.
The Starlette shape does not port directly. There the connection is still a positional argument after self, so the wrapper forwards *args, **kwargs and finds the connection by isinstance. Here the method receives no request at all, so the wrapper has to fall back to args[0].request when args[0] is a web.View. Flask's MethodView needs neither, since that integration reads the child container from flask.g.
Work
- Red: a test with a
web.View subclass whose get uses @inject, under the 100% coverage gate.
- Wrapper forwards arguments unchanged; the request is the first positional
web.Request, else .request of a first positional web.View; else a TypeError naming the handler.
- README
inject row and a class-based example; same for the aiohttp page on the modern-di docs site (separate PR).
@injectsupports function handlers only. Applied to a method of anaiohttp.web.Viewsubclass it fails on the first request rather than being unsupported:and the client gets a 500. Reproduced against aiohttp 3.14.3 with a
Viewwhosegettakes oneFromDIparameter, registered withapp.router.add_view.Why
injectreturnsasync def wrapper(request). aiohttp'sView._itercalls the bound method with no arguments (ret = await method()), so the wrapper receives onlyself, andfetch_request_container(self)doesself[_CONTAINER_REQUEST_KEY]on the view instance. The request is onself.request; nothing reads it from there.Prior art
modern-python/modern-di-starlette#28 fixed the same class of bug for Starlette's
HTTPEndpoint/WebSocketEndpoint, and its issue (modern-di-starlette#18) named "a second integration in the org needs an injection path for a class-based handler" as the revisit trigger. This is that case.The Starlette shape does not port directly. There the connection is still a positional argument after
self, so the wrapper forwards*args, **kwargsand finds the connection byisinstance. Here the method receives no request at all, so the wrapper has to fall back toargs[0].requestwhenargs[0]is aweb.View. Flask'sMethodViewneeds neither, since that integration reads the child container fromflask.g.Work
web.Viewsubclass whosegetuses@inject, under the 100% coverage gate.web.Request, else.requestof a first positionalweb.View; else aTypeErrornaming the handler.injectrow and a class-based example; same for the aiohttp page on the modern-di docs site (separate PR).