1+ import contextvars
12import dataclasses
23import typing
34from collections .abc import Awaitable , Callable
1314
1415faststream_message_provider = providers .ContextProvider (scope = Scope .REQUEST , context_type = faststream .StreamMessage )
1516
16- # Keys under which the containers live in FastStream's ``ContextRepo``. Each is
17- # written in one place and read in another; naming them keeps writer and reader
18- # in provable agreement instead of relying on two matching string literals.
1917_ROOT_CONTAINER_KEY = "modern_di_root_container"
20- _REQUEST_CONTAINER_KEY = "modern_di_request_container"
18+ _request_container : contextvars .ContextVar [Container | None ] = contextvars .ContextVar (
19+ "modern_di_request_container" , default = None
20+ )
2121
2222_MISSING_REQUEST_CONTAINER = (
2323 "No request container for this message, so the DI middleware did not run for it. "
@@ -56,11 +56,14 @@ async def consume_scope(
5656 async with self .di_container .build_child_container (
5757 scope = match .scope , context = match .context
5858 ) as request_container :
59- with self .context .scope (_REQUEST_CONTAINER_KEY , request_container ):
59+ token = _request_container .set (request_container )
60+ try :
6061 return typing .cast (
6162 typing .AsyncIterator [DecodedMessage ],
6263 await call_next (msg ),
6364 )
65+ finally :
66+ _request_container .reset (token )
6467
6568
6669def fetch_di_container (app_ : faststream .FastStream | AsgiFastStream ) -> Container :
@@ -79,7 +82,7 @@ def setup_di(
7982
8083 # Installed on startup rather than here so a broker added after ``setup_di`` is covered,
8184 # and skipped where present so a restart adds no second copy: docs/adr/0002-install-middleware-on-startup.md
82- def install_middleware () -> None :
85+ async def install_middleware () -> None :
8386 if not app .brokers :
8487 raise RuntimeError (_NO_BROKER_AT_STARTUP )
8588 for broker in app .brokers :
@@ -91,7 +94,7 @@ def install_middleware() -> None:
9194 close_registered = False
9295
9396 # Appended on startup so the close runs after every after_shutdown hook registered before it.
94- def open_root () -> None :
97+ async def open_root () -> None :
9598 nonlocal close_registered
9699 container .open ()
97100 if not close_registered :
@@ -106,8 +109,8 @@ def open_root() -> None:
106109class Dependency (typing .Generic [T_co ]):
107110 marker : integrations .Marker [T_co ]
108111
109- async def __call__ (self , context : faststream . ContextRepo ) -> T_co :
110- request_container : Container | None = context . get_local ( _REQUEST_CONTAINER_KEY )
112+ async def __call__ (self ) -> T_co :
113+ request_container = _request_container . get ( )
111114 if request_container is None :
112115 raise RuntimeError (_MISSING_REQUEST_CONTAINER )
113116 return self .marker .resolve (request_container )
0 commit comments