1616 _raise_on_status_error ,
1717)
1818from httpware .decoders import ResponseDecoder
19- from httpware .errors import DecodeError , TransportError
19+ from httpware .errors import DecodeError , MissingDecoderError , TransportError
2020from httpware .middleware import AsyncMiddleware , AsyncNext , Middleware , Next
2121from httpware .middleware .chain import compose , compose_async
2222
@@ -93,7 +93,7 @@ class AsyncClient:
9393
9494 _httpx2_client : httpx2 .AsyncClient
9595 _owns_client : bool
96- _decoder : ResponseDecoder
96+ _decoders : tuple [ ResponseDecoder , ...]
9797 _user_middleware : tuple [AsyncMiddleware , ...]
9898 _dispatch : AsyncNext
9999
@@ -108,7 +108,7 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
108108 limits : httpx2 .Limits | None = None ,
109109 auth : httpx2 .Auth | None = None ,
110110 httpx2_client : httpx2 .AsyncClient | None = None ,
111- decoder : ResponseDecoder | None = None ,
111+ decoders : Sequence [ ResponseDecoder ] | None = None ,
112112 middleware : Sequence [AsyncMiddleware ] = (),
113113 ) -> None :
114114 if httpx2_client is not None :
@@ -144,10 +144,17 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
144144 self ._httpx2_client = httpx2 .AsyncClient (** kwargs )
145145 self ._owns_client = True
146146
147- self ._decoder = decoder if decoder is not None else _default_pydantic_decoder ()
147+ self ._decoders = tuple ( decoders ) if decoders is not None else _build_default_decoders ()
148148 self ._user_middleware = tuple (middleware )
149149 self ._dispatch = compose_async (self ._user_middleware , self ._terminal )
150150
151+ def _dispatch_decoder (self , model : type ) -> ResponseDecoder | None :
152+ """Walk `_decoders` and return the first decoder claiming `model`, or None."""
153+ for decoder in self ._decoders :
154+ if decoder .can_decode (model ):
155+ return decoder
156+ return None
157+
151158 async def _terminal (self , request : httpx2 .Request ) -> httpx2 .Response :
152159 try :
153160 async with _httpx2_exception_mapper ():
@@ -172,11 +179,19 @@ async def send(
172179 response_model : type [T ] | None = None ,
173180 ) -> httpx2 .Response | T :
174181 """Send `request` through the middleware chain. Decode if `response_model` is set."""
175- response = await self ._dispatch (request )
176182 if response_model is None :
177- return response
183+ return await self ._dispatch (request )
184+
185+ decoder = self ._dispatch_decoder (response_model )
186+ if decoder is None :
187+ raise MissingDecoderError (
188+ model = response_model ,
189+ registered_names = tuple (type (d ).__name__ for d in self ._decoders ),
190+ )
191+
192+ response = await self ._dispatch (request )
178193 try :
179- return self . _decoder .decode (response .content , response_model )
194+ return decoder .decode (response .content , response_model )
180195 except Exception as exc :
181196 raise DecodeError (response = response , model = response_model , original = exc ) from exc
182197
@@ -195,9 +210,16 @@ async def send_with_response(
195210 Not for streaming responses — decodes ``response.content``, which
196211 requires the body to be fully read. Use ``stream()`` for streaming.
197212 """
213+ decoder = self ._dispatch_decoder (response_model )
214+ if decoder is None :
215+ raise MissingDecoderError (
216+ model = response_model ,
217+ registered_names = tuple (type (d ).__name__ for d in self ._decoders ),
218+ )
219+
198220 response = await self ._dispatch (request )
199221 try :
200- decoded = self . _decoder .decode (response .content , response_model )
222+ decoded = decoder .decode (response .content , response_model )
201223 except Exception as exc :
202224 raise DecodeError (response = response , model = response_model , original = exc ) from exc
203225 return response , decoded
0 commit comments