11# mypy: disable-error-code = misc
22
3- from json import loads
43from typing import Any , Callable , Dict , get_origin , Optional , Type , Union
54
65import requests
76from nisystemlink .clients import core
8- from pydantic import parse_obj_as
7+ from pydantic import TypeAdapter
98from requests import JSONDecodeError , Response
109from uplink import commands , Consumer , converters , response_handler , utils
1110
@@ -28,7 +27,7 @@ def _handle_http_status(response: Response) -> Optional[Response]:
2827 try :
2928 content = response .json ()
3029 if content and "error" in content :
31- err_obj = core .ApiError .parse_obj (content ["error" ])
30+ err_obj = core .ApiError .model_validate (content ["error" ])
3231 else :
3332 err_obj = None
3433
@@ -41,12 +40,20 @@ def _handle_http_status(response: Response) -> Optional[Response]:
4140 raise core .ApiException (msg , http_status_code = response .status_code )
4241
4342
43+ _type_adapters : Dict [Type , TypeAdapter ] = dict ()
44+
45+
4446class _JsonModelConverter (converters .Factory ):
47+ """A converter that converts between JSON and Pydantic models."""
48+
49+ def __init__ (self ) -> None :
50+ super ().__init__ ()
51+
4552 def create_request_body_converter (
4653 self , _class : Type , _ : commands .RequestDefinition
4754 ) -> Optional [Callable [[JsonModel ], Dict ]]:
4855 def encoder (model : JsonModel ) -> Dict :
49- return loads ( model .json ( by_alias = True , exclude_unset = True ) )
56+ return model .model_dump ( mode = "json" , by_alias = True , exclude_unset = True )
5057
5158 if utils .is_subclass (_class , JsonModel ):
5259 return encoder
@@ -56,15 +63,24 @@ def encoder(model: JsonModel) -> Dict:
5663 def create_response_body_converter (
5764 self , _class : Type , _ : commands .RequestDefinition
5865 ) -> Optional [Callable [[Response ], Any ]]:
59- def decoder (response : Response ) -> Any :
60- try :
61- data = response .json ()
62- except AttributeError :
63- data = response
64-
65- return parse_obj_as (_class , data )
66-
67- if get_origin (_class ) is Union or utils .is_subclass (_class , JsonModel ):
66+ def decoder (response : Union [Response , Any ]) -> Any :
67+ if response is None :
68+ return None
69+
70+ adapter = _type_adapters [_class ]
71+ if isinstance (response , Response ):
72+ if response .status_code == 204 :
73+ return None
74+ return adapter .validate_json (response .text , by_alias = True , strict = True )
75+ else :
76+ # In cases where a return_key is specified, the response will already be parsed into a dict
77+ return adapter .validate_python (response , by_alias = True , strict = True )
78+
79+ origin = get_origin (_class )
80+ modelable_origin = origin is Union or origin is dict or origin is list
81+ if modelable_origin or utils .is_subclass (_class , JsonModel ):
82+ if _type_adapters .get (_class ) is None :
83+ _type_adapters [_class ] = TypeAdapter (_class )
6884 return decoder
6985 else :
7086 return None
0 commit comments