3030 f"{ _FORWARDED_KWARG_NAMES } ; configure the httpx2 client you pass instead."
3131)
3232
33- _DEFAULT_DECODER_MISSING_MESSAGE = (
34- "decoder=None defaults to PydanticDecoder, which requires the "
35- "'pydantic' extra. Either install it (`pip install httpware[pydantic]`) or "
36- "pass an explicit decoder=..."
37- )
38-
39-
40- def _default_pydantic_decoder () -> ResponseDecoder :
41- if not import_checker .is_pydantic_installed :
42- raise ImportError (_DEFAULT_DECODER_MISSING_MESSAGE )
43- from httpware .decoders .pydantic import PydanticDecoder # noqa: PLC0415 — lazy by design
44-
45- return PydanticDecoder ()
46-
4733
4834def _build_default_decoders () -> tuple [ResponseDecoder , ...]:
4935 """Construct the default decoder tuple based on installed extras.
@@ -833,7 +819,7 @@ class Client:
833819
834820 _httpx2_client : httpx2 .Client
835821 _owns_client : bool
836- _decoder : ResponseDecoder
822+ _decoders : tuple [ ResponseDecoder , ...]
837823 _user_middleware : tuple [Middleware , ...]
838824 _dispatch : Next
839825
@@ -848,7 +834,7 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
848834 limits : httpx2 .Limits | None = None ,
849835 auth : httpx2 .Auth | None = None ,
850836 httpx2_client : httpx2 .Client | None = None ,
851- decoder : ResponseDecoder | None = None ,
837+ decoders : Sequence [ ResponseDecoder ] | None = None ,
852838 middleware : Sequence [Middleware ] = (),
853839 ) -> None :
854840 if httpx2_client is not None :
@@ -884,10 +870,17 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
884870 self ._httpx2_client = httpx2 .Client (** kwargs )
885871 self ._owns_client = True
886872
887- self ._decoder = decoder if decoder is not None else _default_pydantic_decoder ()
873+ self ._decoders = tuple ( decoders ) if decoders is not None else _build_default_decoders ()
888874 self ._user_middleware = tuple (middleware )
889875 self ._dispatch = compose (self ._user_middleware , self ._terminal )
890876
877+ def _dispatch_decoder (self , model : type ) -> ResponseDecoder | None :
878+ """Walk `_decoders` and return the first decoder claiming `model`, or None."""
879+ for decoder in self ._decoders :
880+ if decoder .can_decode (model ):
881+ return decoder
882+ return None
883+
891884 def _terminal (self , request : httpx2 .Request ) -> httpx2 .Response :
892885 try :
893886 with _httpx2_exception_mapper_sync ():
@@ -936,11 +929,19 @@ def send(
936929 response_model : type [T ] | None = None ,
937930 ) -> httpx2 .Response | T :
938931 """Send `request` through the middleware chain. Decode if `response_model` is set."""
939- response = self ._dispatch (request )
940932 if response_model is None :
941- return response
933+ return self ._dispatch (request )
934+
935+ decoder = self ._dispatch_decoder (response_model )
936+ if decoder is None :
937+ raise MissingDecoderError (
938+ model = response_model ,
939+ registered_names = tuple (type (d ).__name__ for d in self ._decoders ),
940+ )
941+
942+ response = self ._dispatch (request )
942943 try :
943- return self . _decoder .decode (response .content , response_model )
944+ return decoder .decode (response .content , response_model )
944945 except Exception as exc :
945946 raise DecodeError (response = response , model = response_model , original = exc ) from exc
946947
@@ -959,9 +960,16 @@ def send_with_response(
959960 Not for streaming responses — decodes ``response.content``, which
960961 requires the body to be fully read. Use ``stream()`` for streaming.
961962 """
963+ decoder = self ._dispatch_decoder (response_model )
964+ if decoder is None :
965+ raise MissingDecoderError (
966+ model = response_model ,
967+ registered_names = tuple (type (d ).__name__ for d in self ._decoders ),
968+ )
969+
962970 response = self ._dispatch (request )
963971 try :
964- decoded = self . _decoder .decode (response .content , response_model )
972+ decoded = decoder .decode (response .content , response_model )
965973 except Exception as exc :
966974 raise DecodeError (response = response , model = response_model , original = exc ) from exc
967975 return response , decoded
0 commit comments