Skip to content

Commit 939cec9

Browse files
committed
Schema unmarshal exceptions refactor
1 parent fd99117 commit 939cec9

File tree

4 files changed

+94
-52
lines changed

4 files changed

+94
-52
lines changed

openapi_core/schema/schemas/exceptions.py

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@ class OpenAPISchemaError(OpenAPIMappingError):
77
pass
88

99

10+
class UnmarshallError(OpenAPISchemaError):
11+
"""Unmarshall operation error"""
12+
pass
13+
14+
15+
@attr.s(hash=True)
16+
class UnmarshallValueError(UnmarshallError):
17+
"""Failed to unmarshal value to type"""
18+
value = attr.ib()
19+
type = attr.ib()
20+
original_exception = attr.ib(default=None)
21+
22+
def __str__(self):
23+
return (
24+
"Failed to unmarshal value {value} to type {type}: {exception}"
25+
).format(
26+
value=self.value, type=self.type,
27+
exception=self.original_exception,
28+
)
29+
30+
1031
@attr.s(hash=True)
1132
class NoValidSchema(OpenAPISchemaError):
1233
value = attr.ib()
@@ -33,20 +54,13 @@ def __str__(self):
3354
return self.msg.format(value=self.value, type=self.type)
3455

3556

36-
@attr.s(hash=True)
37-
class InvalidCustomFormatSchemaValue(InvalidSchemaValue):
38-
original_exception = attr.ib()
39-
40-
def __str__(self):
41-
return self.msg.format(value=self.value, type=self.type, exception=self.original_exception)
42-
43-
4457
@attr.s(hash=True)
4558
class UndefinedSchemaProperty(OpenAPISchemaError):
4659
extra_props = attr.ib()
4760

4861
def __str__(self):
49-
return "Extra unexpected properties found in schema: {0}".format(self.extra_props)
62+
return "Extra unexpected properties found in schema: {0}".format(
63+
self.extra_props)
5064

5165

5266
@attr.s(hash=True)
@@ -55,7 +69,8 @@ class InvalidSchemaProperty(OpenAPISchemaError):
5569
original_exception = attr.ib()
5670

5771
def __str__(self):
58-
return "Invalid schema property {0}: {1}".format(self.property_name, self.original_exception)
72+
return "Invalid schema property {0}: {1}".format(
73+
self.property_name, self.original_exception)
5974

6075

6176
@attr.s(hash=True)
@@ -66,14 +81,46 @@ def __str__(self):
6681
return "Missing schema property: {0}".format(self.property_name)
6782

6883

69-
class UnmarshallerError(OpenAPIMappingError):
84+
class UnmarshallerError(UnmarshallError):
85+
"""Unmarshaller error"""
7086
pass
7187

7288

89+
@attr.s(hash=True)
90+
class InvalidCustomFormatSchemaValue(UnmarshallerError):
91+
"""Value failed to format with custom formatter"""
92+
value = attr.ib()
93+
type = attr.ib()
94+
original_exception = attr.ib()
95+
96+
def __str__(self):
97+
return (
98+
"Failed to format value {value} to format {type}: {exception}"
99+
).format(
100+
value=self.value, type=self.type,
101+
exception=self.original_exception,
102+
)
103+
104+
105+
@attr.s(hash=True)
106+
class FormatterNotFoundError(UnmarshallerError):
107+
"""Formatter not found to unmarshal"""
108+
value = attr.ib()
109+
type_format = attr.ib()
110+
111+
def __str__(self):
112+
return (
113+
"Formatter not found for {format} format "
114+
"to unmarshal value {value}"
115+
).format(format=self.type_format, value=self.value)
116+
117+
118+
@attr.s(hash=True)
73119
class UnmarshallerStrictTypeError(UnmarshallerError):
74120
value = attr.ib()
75121
types = attr.ib()
76122

77123
def __str__(self):
78-
return "Value {value} is not one of types {types}".format(
79-
self.value, self.types)
124+
types = ', '.join(list(map(str, self.types)))
125+
return "Value {value} is not one of types: {types}".format(
126+
value=self.value, types=types)

openapi_core/schema/schemas/models.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
from openapi_core.schema.schemas.exceptions import (
1818
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
1919
OpenAPISchemaError, NoValidSchema,
20-
UndefinedItemsSchema, InvalidCustomFormatSchemaValue, InvalidSchemaProperty,
21-
UnmarshallerStrictTypeError,
20+
UndefinedItemsSchema, InvalidSchemaProperty,
21+
UnmarshallerError, UnmarshallValueError, UnmarshallError,
2222
)
2323
from openapi_core.schema.schemas.util import (
2424
forcebool, format_date, format_datetime, format_byte, format_uuid,
@@ -212,12 +212,12 @@ def unmarshal(self, value, custom_formatters=None, strict=True):
212212
warnings.warn("The schema is deprecated", DeprecationWarning)
213213
if value is None:
214214
if not self.nullable:
215-
raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type)
215+
raise UnmarshallError(
216+
"Null value for non-nullable schema", value, self.type)
216217
return self.default
217218

218219
if self.enum and value not in self.enum:
219-
raise InvalidSchemaValue(
220-
"Value {value} not in enum choices: {type}", value, self.enum)
220+
raise UnmarshallError("Invalid value for enum: {0}".format(value))
221221

222222
unmarshal_mapping = self.get_unmarshal_mapping(
223223
custom_formatters=custom_formatters, strict=strict)
@@ -228,12 +228,8 @@ def unmarshal(self, value, custom_formatters=None, strict=True):
228228
unmarshal_callable = unmarshal_mapping[self.type]
229229
try:
230230
unmarshalled = unmarshal_callable(value)
231-
except UnmarshallerStrictTypeError:
232-
raise InvalidSchemaValue(
233-
"Value {value} is not of type {type}", value, self.type)
234-
except ValueError:
235-
raise InvalidSchemaValue(
236-
"Failed to unmarshal value {value} to type {type}", value, self.type)
231+
except ValueError as exc:
232+
raise UnmarshallValueError(value, self.type, exc)
237233

238234
return unmarshalled
239235

@@ -268,7 +264,7 @@ def _unmarshal_any(self, value, custom_formatters=None, strict=True):
268264
for subschema in self.one_of:
269265
try:
270266
unmarshalled = subschema.unmarshal(value, custom_formatters)
271-
except (OpenAPISchemaError, TypeError, ValueError):
267+
except UnmarshallError:
272268
continue
273269
else:
274270
if result is not None:
@@ -285,17 +281,15 @@ def _unmarshal_any(self, value, custom_formatters=None, strict=True):
285281
unmarshal_callable = unmarshal_mapping[schema_type]
286282
try:
287283
return unmarshal_callable(value)
288-
except UnmarshallerStrictTypeError:
289-
continue
290-
except (OpenAPISchemaError, TypeError):
284+
except (UnmarshallError, ValueError):
291285
continue
292286

293287
log.warning("failed to unmarshal any type")
294288
return value
295289

296290
def _unmarshal_collection(self, value, custom_formatters=None, strict=True):
297291
if not isinstance(value, (list, tuple)):
298-
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
292+
raise ValueError("Invalid value for collection: {0}".format(value))
299293

300294
f = functools.partial(
301295
self.items.unmarshal,
@@ -306,7 +300,7 @@ def _unmarshal_collection(self, value, custom_formatters=None, strict=True):
306300
def _unmarshal_object(self, value, model_factory=None,
307301
custom_formatters=None, strict=True):
308302
if not isinstance(value, (dict, )):
309-
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
303+
raise ValueError("Invalid value for object: {0}".format(value))
310304

311305
model_factory = model_factory or ModelFactory()
312306

openapi_core/schema/schemas/unmarshallers.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
OpenAPISchemaError,
77
InvalidSchemaProperty,
88
UnmarshallerStrictTypeError,
9+
FormatterNotFoundError,
910
)
1011
from openapi_core.schema.schemas.util import (
1112
forcebool, format_date, format_datetime, format_byte, format_uuid,
@@ -49,17 +50,12 @@ def __call__(self, value, type_format=SchemaFormat.NONE, strict=True):
4950
formatter = formatters.get(schema_format)
5051

5152
if formatter is None:
52-
raise InvalidSchemaValue(
53-
"Unsupported format {type} unmarshalling "
54-
"for value {value}",
55-
value, type_format)
53+
raise FormatterNotFoundError(value, type_format)
5654

5755
try:
5856
return formatter(value)
5957
except ValueError as exc:
60-
raise InvalidCustomFormatSchemaValue(
61-
"Failed to format value {value} to format {type}: {exception}",
62-
value, type_format, exc)
58+
raise InvalidCustomFormatSchemaValue(value, type_format, exc)
6359

6460
def get_formatters(self):
6561
return self.FORMATTERS

tests/unit/schema/test_schemas.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from openapi_core.extensions.models.models import Model
88
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
99
from openapi_core.schema.schemas.exceptions import (
10-
InvalidSchemaValue, OpenAPISchemaError,
10+
InvalidSchemaValue, OpenAPISchemaError, UnmarshallerStrictTypeError,
11+
UnmarshallValueError, UnmarshallError, InvalidCustomFormatSchemaValue,
12+
FormatterNotFoundError,
1113
)
1214
from openapi_core.schema.schemas.models import Schema
1315

@@ -88,22 +90,22 @@ def test_string_float_invalid(self):
8890
schema = Schema('string')
8991
value = 1.23
9092

91-
with pytest.raises(InvalidSchemaValue):
93+
with pytest.raises(UnmarshallerStrictTypeError):
9294
schema.unmarshal(value)
9395

9496
def test_string_none(self):
9597
schema = Schema('string')
9698
value = None
9799

98-
with pytest.raises(InvalidSchemaValue):
100+
with pytest.raises(UnmarshallError):
99101
schema.unmarshal(value)
100102

101103
def test_string_default(self):
102104
default_value = 'default'
103105
schema = Schema('string', default=default_value)
104106
value = None
105107

106-
with pytest.raises(InvalidSchemaValue):
108+
with pytest.raises(UnmarshallError):
107109
schema.unmarshal(value)
108110

109111
def test_string_default_nullable(self):
@@ -150,7 +152,7 @@ def custom_formatter(value):
150152
schema = Schema('string', schema_format=custom_format)
151153
value = 'x'
152154

153-
with pytest.raises(InvalidSchemaValue):
155+
with pytest.raises(InvalidCustomFormatSchemaValue):
154156
schema.unmarshal(
155157
value, custom_formatters={custom_format: custom_formatter})
156158

@@ -168,7 +170,10 @@ def test_string_format_invalid_value(self):
168170
value = 'x'
169171

170172
with pytest.raises(
171-
InvalidSchemaValue, message='Failed to format value'
173+
FormatterNotFoundError,
174+
message=(
175+
'Formatter not found for custom format to unmarshal value x'
176+
),
172177
):
173178
schema.unmarshal(value)
174179

@@ -184,14 +189,14 @@ def test_integer_string_invalid(self):
184189
schema = Schema('integer')
185190
value = '123'
186191

187-
with pytest.raises(InvalidSchemaValue):
192+
with pytest.raises(UnmarshallerStrictTypeError):
188193
schema.unmarshal(value)
189194

190195
def test_integer_enum_invalid(self):
191196
schema = Schema('integer', enum=[1, 2, 3])
192197
value = '123'
193198

194-
with pytest.raises(InvalidSchemaValue):
199+
with pytest.raises(UnmarshallError):
195200
schema.unmarshal(value)
196201

197202
def test_integer_enum(self):
@@ -206,15 +211,15 @@ def test_integer_enum_string_invalid(self):
206211
schema = Schema('integer', enum=[1, 2, 3])
207212
value = '2'
208213

209-
with pytest.raises(InvalidSchemaValue):
214+
with pytest.raises(UnmarshallError):
210215
schema.unmarshal(value)
211216

212217
def test_integer_default(self):
213218
default_value = '123'
214219
schema = Schema('integer', default=default_value)
215220
value = None
216221

217-
with pytest.raises(InvalidSchemaValue):
222+
with pytest.raises(UnmarshallError):
218223
schema.unmarshal(value)
219224

220225
def test_integer_default_nullable(self):
@@ -230,7 +235,7 @@ def test_integer_invalid(self):
230235
schema = Schema('integer')
231236
value = 'abc'
232237

233-
with pytest.raises(InvalidSchemaValue):
238+
with pytest.raises(UnmarshallerStrictTypeError):
234239
schema.unmarshal(value)
235240

236241
def test_array_valid(self):
@@ -245,14 +250,14 @@ def test_array_of_string_string_invalid(self):
245250
schema = Schema('array', items=Schema('string'))
246251
value = '123'
247252

248-
with pytest.raises(InvalidSchemaValue):
253+
with pytest.raises(UnmarshallValueError):
249254
schema.unmarshal(value)
250255

251256
def test_array_of_integer_string_invalid(self):
252257
schema = Schema('array', items=Schema('integer'))
253258
value = '123'
254259

255-
with pytest.raises(InvalidSchemaValue):
260+
with pytest.raises(UnmarshallValueError):
256261
schema.unmarshal(value)
257262

258263
def test_boolean_valid(self):
@@ -267,7 +272,7 @@ def test_boolean_string_invalid(self):
267272
schema = Schema('boolean')
268273
value = 'True'
269274

270-
with pytest.raises(InvalidSchemaValue):
275+
with pytest.raises(UnmarshallerStrictTypeError):
271276
schema.unmarshal(value)
272277

273278
def test_number_valid(self):
@@ -282,7 +287,7 @@ def test_number_string_invalid(self):
282287
schema = Schema('number')
283288
value = '1.23'
284289

285-
with pytest.raises(InvalidSchemaValue):
290+
with pytest.raises(UnmarshallerStrictTypeError):
286291
schema.unmarshal(value)
287292

288293
def test_number_int(self):

0 commit comments

Comments
 (0)