Skip to content

Commit ffbcd5e

Browse files
committed
multi types schema format unmarshal fix
1 parent b0042ca commit ffbcd5e

File tree

3 files changed

+75
-41
lines changed

3 files changed

+75
-41
lines changed

openapi_core/unmarshalling/schemas/unmarshallers.py

Lines changed: 28 additions & 41 deletions
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,17 +299,10 @@ def evolve(self, schema: Spec) -> "SchemaUnmarshaller":
305299
)
306300

307301
def find_format(self, value: Any) -> Optional[str]:
308-
for schema in self.iter_valid_schemas(value):
309-
if "format" in schema:
302+
primitive_type = self.schema_validator.get_primitive_type(value)
303+
if primitive_type != "string":
304+
return None
305+
for schema in self.schema_validator.iter_valid_schemas(value):
306+
if "format" in schema and schema.getkey("type") == primitive_type:
310307
return str(schema.getkey("format"))
311308
return None
312-
313-
def iter_valid_schemas(self, value: Any) -> Iterator[Spec]:
314-
yield self.schema
315-
316-
one_of_schema = self.schema_validator.get_one_of_schema(value)
317-
if one_of_schema is not None:
318-
yield one_of_schema
319-
320-
yield from self.schema_validator.iter_any_of_schemas(value)
321-
yield from self.schema_validator.iter_all_of_schemas(value)

openapi_core/validation/schemas/validators.py

Lines changed: 26 additions & 0 deletions
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 get_one_of_schema(
8298
self,
8399
value: Any,
@@ -133,3 +149,13 @@ def iter_all_of_schemas(
133149
log.warning("invalid allOf schema found")
134150
else:
135151
yield subschema
152+
153+
def iter_valid_schemas(self, value: Any) -> Iterator[Spec]:
154+
yield self.schema
155+
156+
one_of_schema = self.schema_validator.get_one_of_schema(value)
157+
if one_of_schema is not None:
158+
yield one_of_schema
159+
160+
yield from self.schema_validator.iter_any_of_schemas(value)
161+
yield from self.schema_validator.iter_all_of_schemas(value)

tests/integration/unmarshalling/test_unmarshallers.py

Lines changed: 21 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy