1
1
from __future__ import annotations
2
2
3
3
from dataclasses import fields , is_dataclass
4
- from typing import Any , Optional , Type , TypeVar , Union
4
+ from inspect import isclass
5
+ from typing import Any , Dict , List , Optional , Type , TypeVar , Union
5
6
6
7
import humps
7
8
from quart import current_app
@@ -160,7 +161,7 @@ def model_dump(
160
161
161
162
162
163
def model_load (
163
- data : dict ,
164
+ data : Union [ dict , list ] ,
164
165
model_class : Type [T ],
165
166
exception_class : Type [Exception ],
166
167
* ,
@@ -171,26 +172,10 @@ def model_load(
171
172
data = humps .decamelize (data )
172
173
173
174
try :
174
- if (
175
- is_pydantic_dataclass (model_class )
176
- or issubclass (model_class , BaseModel )
177
- or (
178
- (isinstance (model_class , (list , dict )) or is_dataclass (model_class ))
179
- and PYDANTIC_INSTALLED
180
- and preference != "msgspec"
181
- )
182
- ):
183
- return TypeAdapter (model_class ).validate_python (data ) # type: ignore
184
- elif (
185
- issubclass (model_class , Struct )
186
- or is_attrs (model_class )
187
- or (
188
- (isinstance (model_class , (list , dict )) or is_dataclass (model_class ))
189
- and MSGSPEC_INSTALLED
190
- and preference != "pydantic"
191
- )
192
- ):
193
- return convert (data , model_class , strict = False ) # type: ignore
175
+ if _use_pydantic (model_class , preference ):
176
+ return TypeAdapter (model_class ).validate_python (data )
177
+ elif _use_msgspec (model_class , preference ):
178
+ return convert (data , model_class , strict = False )
194
179
elif not PYDANTIC_INSTALLED and not MSGSPEC_INSTALLED :
195
180
raise TypeError (f"Cannot load { model_class } - try installing msgspec or pydantic" )
196
181
else :
@@ -200,19 +185,9 @@ def model_load(
200
185
201
186
202
187
def model_schema (model_class : Type [Model ], * , preference : Optional [str ] = None ) -> dict :
203
- if (
204
- is_pydantic_dataclass (model_class )
205
- or issubclass (model_class , BaseModel )
206
- or (isinstance (model_class , (list , dict )) and preference != "msgspec" )
207
- or (is_dataclass (model_class ) and preference != "msgspec" )
208
- ):
188
+ if _use_pydantic (model_class , preference ):
209
189
return TypeAdapter (model_class ).json_schema (ref_template = PYDANTIC_REF_TEMPLATE )
210
- elif (
211
- issubclass (model_class , Struct )
212
- or is_attrs (model_class )
213
- or (isinstance (model_class , (list , dict )) and preference != "pydantic" )
214
- or (is_dataclass (model_class ) and preference != "pydantic" )
215
- ):
190
+ elif _use_msgspec (model_class , preference ):
216
191
_ , schema = schema_components ([model_class ], ref_template = MSGSPEC_REF_TEMPLATE )
217
192
return list (schema .values ())[0 ]
218
193
elif not PYDANTIC_INSTALLED and not MSGSPEC_INSTALLED :
@@ -230,12 +205,12 @@ def convert_headers(
230
205
fields_ = set (model_class .__pydantic_fields__ .keys ())
231
206
elif is_dataclass (model_class ):
232
207
fields_ = {field .name for field in fields (model_class )}
233
- elif issubclass (model_class , BaseModel ):
208
+ elif isclass ( model_class ) and issubclass (model_class , BaseModel ):
234
209
fields_ = set (model_class .model_fields .keys ())
210
+ elif isclass (model_class ) and issubclass (model_class , Struct ):
211
+ fields_ = set (model_class .__struct_fields__ )
235
212
elif is_attrs (model_class ):
236
213
fields_ = {field .name for field in attrs_fields (model_class )}
237
- elif issubclass (model_class , Struct ):
238
- fields_ = set (model_class .__struct_fields__ )
239
214
else :
240
215
raise TypeError (f"Cannot convert to { model_class } " )
241
216
@@ -252,3 +227,32 @@ def convert_headers(
252
227
return model_class (** result )
253
228
except (TypeError , MsgSpecValidationError , ValueError ) as error :
254
229
raise exception_class (error )
230
+
231
+
232
+ def _is_list_or_dict (type_ : Type ) -> bool :
233
+ origin = getattr (type_ , "__origin__" , None )
234
+ return origin in (dict , Dict , list , List )
235
+
236
+
237
+ def _use_pydantic (model_class : Type , preference : Optional [str ]) -> bool :
238
+ return (
239
+ is_pydantic_dataclass (model_class )
240
+ or (isclass (model_class ) and issubclass (model_class , BaseModel ))
241
+ or (
242
+ (_is_list_or_dict (model_class ) or is_dataclass (model_class ))
243
+ and PYDANTIC_INSTALLED
244
+ and preference != "msgspec"
245
+ )
246
+ )
247
+
248
+
249
+ def _use_msgspec (model_class : Type , preference : Optional [str ]) -> bool :
250
+ return (
251
+ (isclass (model_class ) and issubclass (model_class , Struct ))
252
+ or is_attrs (model_class )
253
+ or (
254
+ (_is_list_or_dict (model_class ) or is_dataclass (model_class ))
255
+ and MSGSPEC_INSTALLED
256
+ and preference != "pydantic"
257
+ )
258
+ )
0 commit comments