Skip to content

Commit 48ef16a

Browse files
committed
multi types schema format unmarshal fix
1 parent c9e3164 commit 48ef16a

File tree

3 files changed

+74
-30
lines changed

3 files changed

+74
-30
lines changed

Diff for: openapi_core/unmarshalling/schemas/unmarshallers.py

+27-30
Original file line numberDiff line numberDiff line change
@@ -140,34 +140,15 @@ def _unmarshal_properties(
140140

141141
class MultiTypeUnmarshaller(PrimitiveUnmarshaller):
142142
def __call__(self, value: Any) -> Any:
143-
unmarshaller = self._get_best_unmarshaller(value)
143+
primitive_type = self.schema_validator.get_primitive_type(value)
144+
unmarshaller = self.schema_unmarshaller.get_type_unmarshaller(
145+
primitive_type
146+
)
144147
return unmarshaller(value)
145148

146-
@property
147-
def type(self) -> List[str]:
148-
types = self.schema.getkey("type", ["any"])
149-
assert isinstance(types, list)
150-
return types
151-
152-
def _get_best_unmarshaller(self, value: Any) -> "PrimitiveUnmarshaller":
153-
for schema_type in self.type:
154-
result = self.schema_validator.type_validator(
155-
value, type_override=schema_type
156-
)
157-
if not result:
158-
continue
159-
result = self.schema_validator.format_validator(value)
160-
if not result:
161-
continue
162-
return self.schema_unmarshaller.get_type_unmarshaller(schema_type)
163-
164-
raise UnmarshallerError("Unmarshaller not found for type(s)")
165-
166149

167150
class AnyUnmarshaller(MultiTypeUnmarshaller):
168-
@property
169-
def type(self) -> List[str]:
170-
return self.schema_unmarshaller.types_unmarshaller.get_types()
151+
pass
171152

172153

173154
class TypesUnmarshaller:
@@ -187,7 +168,7 @@ def __init__(
187168
def get_types(self) -> List[str]:
188169
return list(self.unmarshallers.keys())
189170

190-
def get_unmarshaller(
171+
def get_unmarshaller_cls(
191172
self,
192173
schema_type: Optional[Union[Iterable[str], str]],
193174
) -> Type["PrimitiveUnmarshaller"]:
@@ -222,8 +203,8 @@ def unmarshal(self, schema_format: str, value: Any) -> Any:
222203
return value
223204
try:
224205
return format_unmarshaller(value)
225-
except (ValueError, TypeError) as exc:
226-
raise FormatUnmarshalError(value, schema_format, exc)
206+
except (AttributeError, ValueError, TypeError) as exc:
207+
return value
227208

228209
def get_unmarshaller(
229210
self, schema_format: str
@@ -281,19 +262,32 @@ def unmarshal(self, value: Any) -> Any:
281262
(isinstance(value, bytes) and schema_format in ["binary", "byte"])
282263
):
283264
return typed
284-
return self.formats_unmarshaller.unmarshal(schema_format, typed)
265+
266+
format_unmarshaller = self.get_format_unmarshaller(schema_format)
267+
if format_unmarshaller is None:
268+
return typed
269+
try:
270+
return format_unmarshaller(typed)
271+
except (AttributeError, ValueError, TypeError):
272+
return typed
285273

286274
def get_type_unmarshaller(
287275
self,
288276
schema_type: Optional[Union[Iterable[str], str]],
289277
) -> PrimitiveUnmarshaller:
290-
klass = self.types_unmarshaller.get_unmarshaller(schema_type)
278+
klass = self.types_unmarshaller.get_unmarshaller_cls(schema_type)
291279
return klass(
292280
self.schema,
293281
self.schema_validator,
294282
self,
295283
)
296284

285+
def get_format_unmarshaller(
286+
self,
287+
schema_format: str,
288+
) -> Optional[FormatUnmarshaller]:
289+
return self.formats_unmarshaller.get_unmarshaller(schema_format)
290+
297291
def evolve(self, schema: Spec) -> "SchemaUnmarshaller":
298292
cls = self.__class__
299293

@@ -305,7 +299,10 @@ def evolve(self, schema: Spec) -> "SchemaUnmarshaller":
305299
)
306300

307301
def find_format(self, value: Any) -> Optional[str]:
302+
primitive_type = self.schema_validator.get_primitive_type(value)
303+
if primitive_type != "string":
304+
return None
308305
for schema in self.schema_validator.iter_valid_schemas(value):
309-
if "format" in schema:
306+
if "format" in schema and schema.getkey("type") == primitive_type:
310307
return str(schema.getkey("format"))
311308
return None

Diff for: openapi_core/validation/schemas/validators.py

+26
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ def format_validator_callable(self) -> FormatValidator:
7878

7979
return lambda x: True
8080

81+
def get_primitive_type(self, value: Any) -> str:
82+
schema_types = self.schema.getkey("type")
83+
if isinstance(schema_types, str):
84+
return schema_types
85+
if schema_types is None:
86+
schema_types = sorted(self.validator.TYPE_CHECKER._type_checkers)
87+
assert isinstance(schema_types, list)
88+
for schema_type in schema_types:
89+
result = self.type_validator(value, type_override=schema_type)
90+
if not result:
91+
continue
92+
result = self.format_validator(value)
93+
if not result:
94+
continue
95+
return schema_type
96+
8197
def iter_valid_schemas(self, value: Any) -> Iterator[Spec]:
8298
yield self.schema
8399

@@ -143,3 +159,13 @@ def iter_all_of_schemas(
143159
log.warning("invalid allOf schema found")
144160
else:
145161
yield subschema
162+
163+
def iter_valid_schemas(self, value: Any) -> Iterator[Spec]:
164+
yield self.schema
165+
166+
one_of_schema = self.schema_validator.get_one_of_schema(value)
167+
if one_of_schema is not None:
168+
yield one_of_schema
169+
170+
yield from self.schema_validator.iter_any_of_schemas(value)
171+
yield from self.schema_validator.iter_all_of_schemas(value)

Diff for: tests/integration/unmarshalling/test_unmarshallers.py

+21
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,27 @@ def test_nultiple_types_invalid(self, unmarshallers_factory, types, value):
20572057
assert len(exc_info.value.schema_errors) == 1
20582058
assert "is not of type" in exc_info.value.schema_errors[0].message
20592059

2060+
@pytest.mark.parametrize(
2061+
"types,format,value,expected",
2062+
[
2063+
(["string", "null"], "date", None, None),
2064+
(["string", "null"], "date", "2018-12-13", date(2018, 12, 13)),
2065+
],
2066+
)
2067+
def test_multiple_types_format_valid_or_ignored(
2068+
self, unmarshallers_factory, types, format, value, expected
2069+
):
2070+
schema = {
2071+
"type": types,
2072+
"format": format,
2073+
}
2074+
spec = Spec.from_dict(schema, validator=None)
2075+
unmarshaller = unmarshallers_factory.create(spec)
2076+
2077+
result = unmarshaller.unmarshal(value)
2078+
2079+
assert result == expected
2080+
20602081
def test_any_null(self, unmarshallers_factory):
20612082
schema = {}
20622083
spec = Spec.from_dict(schema, validator=None)

0 commit comments

Comments
 (0)