Skip to content

Commit 50e54dd

Browse files
committed
Multi type unmarshaller
1 parent 7db11f4 commit 50e54dd

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

openapi_core/unmarshalling/schemas/factories.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import warnings
22
from typing import Any
33
from typing import Dict
4+
from typing import Iterable
45
from typing import Optional
56
from typing import Type
67
from typing import Union
@@ -30,6 +31,9 @@
3031
from openapi_core.unmarshalling.schemas.unmarshallers import (
3132
IntegerUnmarshaller,
3233
)
34+
from openapi_core.unmarshalling.schemas.unmarshallers import (
35+
MultiTypeUnmarshaller,
36+
)
3337
from openapi_core.unmarshalling.schemas.unmarshallers import NullUnmarshaller
3438
from openapi_core.unmarshalling.schemas.unmarshallers import NumberUnmarshaller
3539
from openapi_core.unmarshalling.schemas.unmarshallers import ObjectUnmarshaller
@@ -89,6 +93,12 @@ def create(
8993
formatter = self.custom_formatters.get(schema_format)
9094

9195
schema_type = type_override or schema.getkey("type", "any")
96+
if isinstance(schema_type, Iterable) and not isinstance(
97+
schema_type, str
98+
):
99+
return MultiTypeUnmarshaller(
100+
schema, validator, formatter, self, context=self.context
101+
)
92102
if schema_type in self.COMPLEX_UNMARSHALLERS:
93103
complex_klass = self.COMPLEX_UNMARSHALLERS[schema_type]
94104
return complex_klass(

openapi_core/unmarshalling/schemas/unmarshallers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,27 @@ def _unmarshal_object(self, value: Any) -> Any:
312312
return properties
313313

314314

315+
class MultiTypeUnmarshaller(ComplexUnmarshaller):
316+
@property
317+
def types_unmarshallers(self) -> List["BaseSchemaUnmarshaller"]:
318+
types = self.schema.getkey("type", ["any"])
319+
unmarshaller = partial(self.unmarshallers_factory.create, self.schema)
320+
return list(map(unmarshaller, types))
321+
322+
def unmarshal(self, value: Any) -> Any:
323+
for unmarshaller in self.types_unmarshallers:
324+
# validate with validator of formatter (usualy type validator)
325+
try:
326+
unmarshaller._formatter_validate(value)
327+
except ValidateError:
328+
continue
329+
else:
330+
return unmarshaller(value)
331+
332+
log.warning("failed to unmarshal multi type")
333+
return value
334+
335+
315336
class AnyUnmarshaller(ComplexUnmarshaller):
316337

317338
SCHEMA_TYPES_ORDER = [

tests/unit/unmarshalling/test_unmarshal.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,40 @@ def test_null_invalid(self, unmarshaller_factory, value):
856856

857857
with pytest.raises(InvalidSchemaValue):
858858
unmarshaller_factory(spec)(value)
859+
860+
@pytest.mark.parametrize(
861+
"types,value",
862+
[
863+
(["string", "null"], "string"),
864+
(["number", "null"], 2),
865+
(["number", "null"], 3.14),
866+
(["boolean", "null"], True),
867+
(["array", "null"], [1, 2]),
868+
(["object", "null"], {}),
869+
],
870+
)
871+
def test_nultiple_types(self, unmarshaller_factory, types, value):
872+
schema = {"type": types}
873+
spec = Spec.from_dict(schema)
874+
875+
result = unmarshaller_factory(spec)(value)
876+
877+
assert result == value
878+
879+
@pytest.mark.parametrize(
880+
"types,value",
881+
[
882+
(["string", "null"], 2),
883+
(["number", "null"], "string"),
884+
(["number", "null"], True),
885+
(["boolean", "null"], 3.14),
886+
(["array", "null"], {}),
887+
(["object", "null"], [1, 2]),
888+
],
889+
)
890+
def test_nultiple_types_invalid(self, unmarshaller_factory, types, value):
891+
schema = {"type": types}
892+
spec = Spec.from_dict(schema)
893+
894+
with pytest.raises(InvalidSchemaValue):
895+
unmarshaller_factory(spec)(value)

0 commit comments

Comments
 (0)