3
3
from typing import TYPE_CHECKING
4
4
from typing import Any
5
5
from typing import Iterable
6
+ from typing import Iterator
6
7
from typing import List
7
8
from typing import Optional
8
9
from typing import cast
31
32
)
32
33
from openapi_core .unmarshalling .schemas .exceptions import InvalidSchemaValue
33
34
from openapi_core .unmarshalling .schemas .exceptions import UnmarshalError
35
+ from openapi_core .unmarshalling .schemas .exceptions import UnmarshallerError
34
36
from openapi_core .unmarshalling .schemas .exceptions import ValidateError
35
37
from openapi_core .unmarshalling .schemas .formatters import Formatter
36
38
from openapi_core .unmarshalling .schemas .util import format_byte
@@ -61,24 +63,25 @@ def __init__(
61
63
):
62
64
self .schema = schema
63
65
self .validator = validator
64
- self .format = schema .getkey ("format" )
66
+ self .schema_format = schema .getkey ("format" )
65
67
66
68
if formatter is None :
67
- if self .format not in self .FORMATTERS :
68
- raise FormatterNotFoundError (self .format )
69
- self .formatter = self .FORMATTERS [self .format ]
69
+ if self .schema_format not in self .FORMATTERS :
70
+ raise FormatterNotFoundError (self .schema_format )
71
+ self .formatter = self .FORMATTERS [self .schema_format ]
70
72
else :
71
73
self .formatter = formatter
72
74
73
75
def __call__ (self , value : Any ) -> Any :
74
- if value is None :
75
- return
76
-
77
76
self .validate (value )
78
77
78
+ # skip unmarshalling for nullable in OpenAPI 3.0
79
+ if value is None and self .schema .getkey ("nullable" , False ):
80
+ return value
81
+
79
82
return self .unmarshal (value )
80
83
81
- def _formatter_validate (self , value : Any ) -> None :
84
+ def _validate_format (self , value : Any ) -> None :
82
85
result = self .formatter .validate (value )
83
86
if not result :
84
87
schema_type = self .schema .getkey ("type" , "any" )
@@ -91,11 +94,14 @@ def validate(self, value: Any) -> None:
91
94
schema_type = self .schema .getkey ("type" , "any" )
92
95
raise InvalidSchemaValue (value , schema_type , schema_errors = errors )
93
96
94
- def unmarshal (self , value : Any ) -> Any :
97
+ def format (self , value : Any ) -> Any :
95
98
try :
96
- return self .formatter .unmarshal (value )
97
- except ValueError as exc :
98
- raise InvalidSchemaFormatValue (value , self .format , exc )
99
+ return self .formatter .format (value )
100
+ except (ValueError , TypeError ) as exc :
101
+ raise InvalidSchemaFormatValue (value , self .schema_format , exc )
102
+
103
+ def unmarshal (self , value : Any ) -> Any :
104
+ return self .format (value )
99
105
100
106
101
107
class StringUnmarshaller (BaseSchemaUnmarshaller ):
@@ -192,10 +198,8 @@ def items_unmarshaller(self) -> "BaseSchemaUnmarshaller":
192
198
items_schema = self .schema .get ("items" , Spec .from_dict ({}))
193
199
return self .unmarshallers_factory .create (items_schema )
194
200
195
- def __call__ (self , value : Any ) -> Optional [List [Any ]]:
196
- value = super ().__call__ (value )
197
- if value is None and self .schema .getkey ("nullable" , False ):
198
- return None
201
+ def unmarshal (self , value : Any ) -> Optional [List [Any ]]:
202
+ value = super ().unmarshal (value )
199
203
return list (map (self .items_unmarshaller , value ))
200
204
201
205
@@ -210,38 +214,31 @@ def object_class_factory(self) -> ModelPathFactory:
210
214
return ModelPathFactory ()
211
215
212
216
def unmarshal (self , value : Any ) -> Any :
213
- properties = self .unmarshal_raw (value )
217
+ properties = self .format (value )
214
218
215
219
fields : Iterable [str ] = properties and properties .keys () or []
216
220
object_class = self .object_class_factory .create (self .schema , fields )
217
221
218
222
return object_class (** properties )
219
223
220
- def unmarshal_raw (self , value : Any ) -> Any :
221
- try :
222
- value = self .formatter .unmarshal (value )
223
- except ValueError as exc :
224
- schema_format = self .schema .getkey ("format" )
225
- raise InvalidSchemaFormatValue (value , schema_format , exc )
226
- else :
227
- return self ._unmarshal_object (value )
224
+ def format (self , value : Any ) -> Any :
225
+ formatted = super ().format (value )
226
+ return self ._unmarshal_properties (formatted )
228
227
229
228
def _clone (self , schema : Spec ) -> "ObjectUnmarshaller" :
230
229
return cast (
231
230
"ObjectUnmarshaller" ,
232
231
self .unmarshallers_factory .create (schema , "object" ),
233
232
)
234
233
235
- def _unmarshal_object (self , value : Any ) -> Any :
234
+ def _unmarshal_properties (self , value : Any ) -> Any :
236
235
properties = {}
237
236
238
237
if "oneOf" in self .schema :
239
238
one_of_properties = None
240
239
for one_of_schema in self .schema / "oneOf" :
241
240
try :
242
- unmarshalled = self ._clone (one_of_schema ).unmarshal_raw (
243
- value
244
- )
241
+ unmarshalled = self ._clone (one_of_schema ).format (value )
245
242
except (UnmarshalError , ValueError ):
246
243
pass
247
244
else :
@@ -259,9 +256,7 @@ def _unmarshal_object(self, value: Any) -> Any:
259
256
any_of_properties = None
260
257
for any_of_schema in self .schema / "anyOf" :
261
258
try :
262
- unmarshalled = self ._clone (any_of_schema ).unmarshal_raw (
263
- value
264
- )
259
+ unmarshalled = self ._clone (any_of_schema ).format (value )
265
260
except (UnmarshalError , ValueError ):
266
261
pass
267
262
else :
@@ -319,21 +314,36 @@ def types_unmarshallers(self) -> List["BaseSchemaUnmarshaller"]:
319
314
unmarshaller = partial (self .unmarshallers_factory .create , self .schema )
320
315
return list (map (unmarshaller , types ))
321
316
322
- def unmarshal (self , value : Any ) -> Any :
323
- for unmarshaller in self .types_unmarshallers :
317
+ @property
318
+ def type (self ) -> List [str ]:
319
+ types = self .schema .getkey ("type" , ["any" ])
320
+ assert isinstance (types , list )
321
+ return types
322
+
323
+ def _get_unmarshallers_iter (self ) -> Iterator ["BaseSchemaUnmarshaller" ]:
324
+ for schema_type in self .type :
325
+ yield self .unmarshallers_factory .create (
326
+ self .schema , type_override = schema_type
327
+ )
328
+
329
+ def _get_best_unmarshaller (self , value : Any ) -> "BaseSchemaUnmarshaller" :
330
+ for unmarshaller in self ._get_unmarshallers_iter ():
324
331
# validate with validator of formatter (usualy type validator)
325
332
try :
326
- unmarshaller ._formatter_validate (value )
333
+ unmarshaller ._validate_format (value )
327
334
except ValidateError :
328
335
continue
329
336
else :
330
- return unmarshaller ( value )
337
+ return unmarshaller
331
338
332
- log .warning ("failed to unmarshal multi type" )
333
- return value
339
+ raise UnmarshallerError ("Unmarshaller not found for type(s)" )
340
+
341
+ def unmarshal (self , value : Any ) -> Any :
342
+ unmarshaller = self ._get_best_unmarshaller (value )
343
+ return unmarshaller (value )
334
344
335
345
336
- class AnyUnmarshaller (ComplexUnmarshaller ):
346
+ class AnyUnmarshaller (MultiTypeUnmarshaller ):
337
347
338
348
SCHEMA_TYPES_ORDER = [
339
349
"object" ,
@@ -344,6 +354,10 @@ class AnyUnmarshaller(ComplexUnmarshaller):
344
354
"string" ,
345
355
]
346
356
357
+ @property
358
+ def type (self ) -> List [str ]:
359
+ return self .SCHEMA_TYPES_ORDER
360
+
347
361
def unmarshal (self , value : Any ) -> Any :
348
362
one_of_schema = self ._get_one_of_schema (value )
349
363
if one_of_schema :
@@ -357,20 +371,7 @@ def unmarshal(self, value: Any) -> Any:
357
371
if all_of_schema :
358
372
return self .unmarshallers_factory .create (all_of_schema )(value )
359
373
360
- for schema_type in self .SCHEMA_TYPES_ORDER :
361
- unmarshaller = self .unmarshallers_factory .create (
362
- self .schema , type_override = schema_type
363
- )
364
- # validate with validator of formatter (usualy type validator)
365
- try :
366
- unmarshaller ._formatter_validate (value )
367
- except ValidateError :
368
- continue
369
- else :
370
- return unmarshaller (value )
371
-
372
- log .warning ("failed to unmarshal any type" )
373
- return value
374
+ return super ().unmarshal (value )
374
375
375
376
def _get_one_of_schema (self , value : Any ) -> Optional [Spec ]:
376
377
if "oneOf" not in self .schema :
0 commit comments