1515from openapi_core .unmarshalling .schemas .datatypes import (
1616 FormatUnmarshallersDict ,
1717)
18+ from openapi_core .validation .schemas .datatypes import ValidationState
1819from openapi_core .validation .schemas .validators import SchemaValidator
1920
2021log = logging .getLogger (__name__ )
@@ -39,6 +40,14 @@ class ArrayUnmarshaller(PrimitiveUnmarshaller):
3940 def __call__ (self , value : Any ) -> Optional [List [Any ]]:
4041 return list (map (self .items_unmarshaller .unmarshal , value ))
4142
43+ def unmarshal_state (self , state : ValidationState ) -> Optional [List [Any ]]:
44+ if state .item_states :
45+ return [
46+ self .items_unmarshaller .unmarshal_state (item_state )
47+ for item_state in state .item_states
48+ ]
49+ return self (value = state .value )
50+
4251 @property
4352 def items_unmarshaller (self ) -> "SchemaUnmarshaller" :
4453 # sometimes we don't have any schema i.e. free-form objects
@@ -55,6 +64,14 @@ def __call__(self, value: Any) -> Any:
5564
5665 return object_class (** properties )
5766
67+ def unmarshal_state (self , state : ValidationState ) -> Any :
68+ properties = self ._unmarshal_properties_from_state (state )
69+
70+ fields : Iterable [str ] = properties and properties .keys () or []
71+ object_class = self .object_class_factory .create (self .schema , fields )
72+
73+ return object_class (** properties )
74+
5875 @property
5976 def object_class_factory (self ) -> ModelPathFactory :
6077 return ModelPathFactory ()
@@ -125,6 +142,94 @@ def _unmarshal_properties(
125142 for prop_name , prop_value in value .items ():
126143 if prop_name in properties :
127144 continue
145+ child_state = state .additional_property_states .get (prop_name )
146+ if child_state is not None :
147+ properties [prop_name ] = (
148+ additional_prop_unmarshaler .unmarshal_state (child_state )
149+ )
150+ continue
151+ properties [prop_name ] = additional_prop_unmarshaler .unmarshal (
152+ prop_value
153+ )
154+
155+ return properties
156+
157+ def _unmarshal_properties_from_state (
158+ self ,
159+ state : ValidationState ,
160+ schema_only : bool = False ,
161+ ) -> Any :
162+ value = state .value
163+ properties = {}
164+
165+ if state .one_of_state is not None :
166+ one_of_properties = self .evolve (
167+ state .one_of_state .schema
168+ )._unmarshal_properties_from_state (
169+ state .one_of_state ,
170+ schema_only = True ,
171+ )
172+ properties .update (one_of_properties )
173+
174+ for any_of_state in state .any_of_states :
175+ any_of_properties = self .evolve (
176+ any_of_state .schema
177+ )._unmarshal_properties_from_state (
178+ any_of_state ,
179+ schema_only = True ,
180+ )
181+ properties .update (any_of_properties )
182+
183+ for all_of_state in state .all_of_states :
184+ all_of_properties = self .evolve (
185+ all_of_state .schema
186+ )._unmarshal_properties_from_state (
187+ all_of_state ,
188+ schema_only = True ,
189+ )
190+ properties .update (all_of_properties )
191+
192+ for prop_name , prop_schema in get_properties (self .schema ).items ():
193+ child_state = state .property_states .get (prop_name )
194+ if child_state is not None :
195+ properties [prop_name ] = self .schema_unmarshaller .evolve (
196+ prop_schema
197+ ).unmarshal_state (child_state )
198+ continue
199+
200+ try :
201+ prop_value = value [prop_name ]
202+ except KeyError :
203+ if "default" not in prop_schema :
204+ continue
205+ prop_value = (prop_schema / "default" ).read_value ()
206+ properties [prop_name ] = self .schema_unmarshaller .evolve (
207+ prop_schema
208+ ).unmarshal (prop_value )
209+
210+ if schema_only :
211+ return properties
212+
213+ additional_properties = self .schema .get ("additionalProperties" , True )
214+ if additional_properties is not False :
215+ if additional_properties is True :
216+ additional_prop_schema = SchemaPath .from_dict (
217+ {"nullable" : True }
218+ )
219+ else :
220+ additional_prop_schema = self .schema / "additionalProperties"
221+ additional_prop_unmarshaler = self .schema_unmarshaller .evolve (
222+ additional_prop_schema
223+ )
224+ for prop_name , prop_value in value .items ():
225+ if prop_name in properties :
226+ continue
227+ child_state = state .additional_property_states .get (prop_name )
228+ if child_state is not None :
229+ properties [prop_name ] = (
230+ additional_prop_unmarshaler .unmarshal_state (child_state )
231+ )
232+ continue
128233 properties [prop_name ] = additional_prop_unmarshaler .unmarshal (
129234 prop_value
130235 )
@@ -143,6 +248,17 @@ def __call__(self, value: Any) -> Any:
143248 )
144249 return unmarshaller (value )
145250
251+ def unmarshal_state (self , state : ValidationState ) -> Any :
252+ primitive_type = state .primitive_type
253+ if primitive_type is None :
254+ return None
255+ unmarshaller = self .schema_unmarshaller .get_type_unmarshaller (
256+ primitive_type
257+ )
258+ if hasattr (unmarshaller , "unmarshal_state" ):
259+ return unmarshaller .unmarshal_state (state )
260+ return unmarshaller (state .value )
261+
146262
147263class AnyUnmarshaller (MultiTypeUnmarshaller ):
148264 pass
@@ -239,7 +355,11 @@ def __init__(
239355 self .formats_unmarshaller = formats_unmarshaller
240356
241357 def unmarshal (self , value : Any ) -> Any :
242- self .schema_validator .validate (value )
358+ state = self .schema_validator .validate_state (value )
359+ return self .unmarshal_state (state )
360+
361+ def unmarshal_state (self , state : ValidationState ) -> Any :
362+ value = state .value
243363
244364 # skip unmarshalling for nullable in OpenAPI 3.0
245365 if value is None and (self .schema / "nullable" ).read_bool (
@@ -249,11 +369,14 @@ def unmarshal(self, value: Any) -> Any:
249369
250370 schema_type = (self .schema / "type" ).read_str_or_list (None )
251371 type_unmarshaller = self .get_type_unmarshaller (schema_type )
252- typed = type_unmarshaller (value )
372+ if hasattr (type_unmarshaller , "unmarshal_state" ):
373+ typed = type_unmarshaller .unmarshal_state (state )
374+ else :
375+ typed = type_unmarshaller (value )
253376 # skip finding format for None
254377 if typed is None :
255378 return None
256- schema_format = self .find_format (value )
379+ schema_format = self .find_format (value , state = state )
257380 if schema_format is None :
258381 return typed
259382 # ignore incompatible formats
@@ -300,7 +423,21 @@ def evolve(self, schema: SchemaPath) -> "SchemaUnmarshaller":
300423 self .formats_unmarshaller ,
301424 )
302425
303- def find_format (self , value : Any ) -> Optional [str ]:
426+ def find_format (
427+ self ,
428+ value : Any ,
429+ state : Optional [ValidationState ] = None ,
430+ ) -> Optional [str ]:
431+ if state is not None :
432+ for schema in self .iter_valid_schemas_from_state (state ):
433+ schema_validator = self .schema_validator .evolve (schema )
434+ primitive_type = schema_validator .get_primitive_type (value )
435+ if primitive_type != "string" :
436+ continue
437+ if "format" in schema :
438+ return (schema / "format" ).read_str ()
439+ return None
440+
304441 for schema in self .schema_validator .iter_valid_schemas (value ):
305442 schema_validator = self .schema_validator .evolve (schema )
306443 primitive_type = schema_validator .get_primitive_type (value )
@@ -309,3 +446,18 @@ def find_format(self, value: Any) -> Optional[str]:
309446 if "format" in schema :
310447 return (schema / "format" ).read_str ()
311448 return None
449+
450+ def iter_valid_schemas_from_state (
451+ self ,
452+ state : ValidationState ,
453+ ) -> Iterable [SchemaPath ]:
454+ yield state .schema
455+
456+ if state .one_of_state is not None :
457+ yield state .one_of_state .schema
458+
459+ for any_of_state in state .any_of_states :
460+ yield any_of_state .schema
461+
462+ for all_of_state in state .all_of_states :
463+ yield all_of_state .schema
0 commit comments