Skip to content

Commit 9f804c7

Browse files
committed
multi types schema format unmarshal fix
1 parent b91e977 commit 9f804c7

File tree

3 files changed

+96
-32
lines changed

3 files changed

+96
-32
lines changed

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

+25-32
Original file line numberDiff line numberDiff line change
@@ -142,34 +142,13 @@ def _unmarshal_properties(
142142

143143
class MultiTypeUnmarshaller(PrimitiveUnmarshaller):
144144
def __call__(self, value: Any) -> Any:
145-
unmarshaller = self._get_best_unmarshaller(value)
145+
primitive_type = self.schema_validator.get_primitive_type(value)
146+
unmarshaller = self.schema_unmarshaller.get_type_unmarshaller(primitive_type)
146147
return unmarshaller(value)
147148

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

169150
class AnyUnmarshaller(MultiTypeUnmarshaller):
170-
@property
171-
def type(self) -> List[str]:
172-
return self.schema_unmarshaller.types_unmarshaller.get_types()
151+
pass
173152

174153

175154
class TypesUnmarshaller:
@@ -189,7 +168,7 @@ def __init__(
189168
def get_types(self) -> List[str]:
190169
return list(self.unmarshallers.keys())
191170

192-
def get_unmarshaller(
171+
def get_unmarshaller_cls(
193172
self,
194173
schema_type: Optional[Union[Iterable[str], str]],
195174
) -> Type["PrimitiveUnmarshaller"]:
@@ -228,8 +207,8 @@ def unmarshal(self, schema_format: str, value: Any) -> Any:
228207
return value
229208
try:
230209
return format_unmarshaller(value)
231-
except (ValueError, TypeError) as exc:
232-
raise FormatUnmarshalError(value, schema_format, exc)
210+
except (AttributeError, ValueError, TypeError) as exc:
211+
return value
233212

234213
def get_unmarshaller(
235214
self, schema_format: str
@@ -288,22 +267,33 @@ def unmarshal(self, value: Any) -> Any:
288267
schema_type = self.schema.getkey("type")
289268
type_unmarshaller = self.get_type_unmarshaller(schema_type)
290269
typed = type_unmarshaller(value)
270+
291271
schema_format = self.find_format(value)
292-
if schema_format is None:
272+
format_unmarshaller = self.get_format_unmarshaller(schema_format)
273+
if format_unmarshaller is None:
274+
return typed
275+
try:
276+
return format_unmarshaller(typed)
277+
except (AttributeError, ValueError, TypeError):
293278
return typed
294-
return self.formats_unmarshaller.unmarshal(schema_format, typed)
295279

296280
def get_type_unmarshaller(
297281
self,
298282
schema_type: Optional[Union[Iterable[str], str]],
299283
) -> PrimitiveUnmarshaller:
300-
klass = self.types_unmarshaller.get_unmarshaller(schema_type)
284+
klass = self.types_unmarshaller.get_unmarshaller_cls(schema_type)
301285
return klass(
302286
self.schema,
303287
self.schema_validator,
304288
self,
305289
)
306290

291+
def get_format_unmarshaller(
292+
self,
293+
schema_format: str,
294+
) -> Optional[FormatUnmarshaller]:
295+
return self.formats_unmarshaller.get_unmarshaller(schema_format)
296+
307297
def evolve(self, schema: Spec) -> "SchemaUnmarshaller":
308298
cls = self.__class__
309299

@@ -315,8 +305,11 @@ def evolve(self, schema: Spec) -> "SchemaUnmarshaller":
315305
)
316306

317307
def find_format(self, value: Any) -> Optional[str]:
318-
for schema in self.iter_valid_schemas(value):
319-
if "format" in schema:
308+
primitive_type = self.schema_validator.get_primitive_type(value)
309+
if primitive_type != "string":
310+
return None
311+
for schema in self.schema_validator.iter_valid_schemas(value):
312+
if "format" in schema and schema.getkey("type") == primitive_type:
320313
return str(schema.getkey("format"))
321314
return None
322315

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

+28
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,24 @@ def format_validator_callable(self) -> FormatValidator:
8383

8484
return lambda x: True
8585

86+
def get_primitive_type(self, value: Any) -> str:
87+
schema_types = self.schema.getkey("type")
88+
if isinstance(schema_types, str):
89+
return schema_types
90+
if schema_types is None:
91+
schema_types = sorted(self.validator.TYPE_CHECKER._type_checkers)
92+
assert isinstance(schema_types, list)
93+
for schema_type in schema_types:
94+
result = self.type_validator(
95+
value, type_override=schema_type
96+
)
97+
if not result:
98+
continue
99+
result = self.format_validator(value)
100+
if not result:
101+
continue
102+
return schema_type
103+
86104
def get_one_of_schema(
87105
self,
88106
value: Any,
@@ -138,3 +156,13 @@ def iter_all_of_schemas(
138156
log.warning("invalid allOf schema found")
139157
else:
140158
yield subschema
159+
160+
def iter_valid_schemas(self, value: Any) -> Iterator[Spec]:
161+
yield self.schema
162+
163+
one_of_schema = self.schema_validator.get_one_of_schema(value)
164+
if one_of_schema is not None:
165+
yield one_of_schema
166+
167+
yield from self.schema_validator.iter_any_of_schemas(value)
168+
yield from self.schema_validator.iter_all_of_schemas(value)

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

+43
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,30 @@ def test_basic_type_formats(
262262

263263
assert result == unmarshalled
264264

265+
@pytest.mark.parametrize(
266+
"type,format,value",
267+
[
268+
("string", "float", "test"),
269+
("string", "double", "test"),
270+
("number", "date", 3),
271+
("number", "date-time", 3),
272+
("number", "uuid", 3),
273+
],
274+
)
275+
def test_basic_type_formats_ignored(
276+
self, unmarshallers_factory, type, format, value
277+
):
278+
schema = {
279+
"type": type,
280+
"format": format,
281+
}
282+
spec = Spec.from_dict(schema, validator=None)
283+
unmarshaller = unmarshallers_factory.create(spec)
284+
285+
result = unmarshaller.unmarshal(value)
286+
287+
assert result == value
288+
265289
@pytest.mark.parametrize(
266290
"type,format,value",
267291
[
@@ -2060,6 +2084,25 @@ def test_nultiple_types_invalid(self, unmarshallers_factory, types, value):
20602084
assert len(exc_info.value.schema_errors) == 1
20612085
assert "is not of type" in exc_info.value.schema_errors[0].message
20622086

2087+
@pytest.mark.parametrize(
2088+
"types,format,value,expected",
2089+
[
2090+
(["string", "null"], "date", None, None),
2091+
(["string", "null"], "date", "2018-12-13", date(2018, 12, 13)),
2092+
],
2093+
)
2094+
def test_multiple_types_format_valid_or_ignored(self, unmarshallers_factory, types, format, value, expected):
2095+
schema = {
2096+
"type": types,
2097+
"format": format,
2098+
}
2099+
spec = Spec.from_dict(schema, validator=None)
2100+
unmarshaller = unmarshallers_factory.create(spec)
2101+
2102+
result = unmarshaller.unmarshal(value)
2103+
2104+
assert result == expected
2105+
20632106
def test_any_null(self, unmarshallers_factory):
20642107
schema = {}
20652108
spec = Spec.from_dict(schema, validator=None)

0 commit comments

Comments
 (0)