Schema unmarshal exceptions refactor

This commit is contained in:
p1c2u 2019-10-20 13:00:14 +01:00
parent fd99117278
commit 939cec94e7
4 changed files with 94 additions and 52 deletions

View file

@ -7,6 +7,27 @@ class OpenAPISchemaError(OpenAPIMappingError):
pass
class UnmarshallError(OpenAPISchemaError):
"""Unmarshall operation error"""
pass
@attr.s(hash=True)
class UnmarshallValueError(UnmarshallError):
"""Failed to unmarshal value to type"""
value = attr.ib()
type = attr.ib()
original_exception = attr.ib(default=None)
def __str__(self):
return (
"Failed to unmarshal value {value} to type {type}: {exception}"
).format(
value=self.value, type=self.type,
exception=self.original_exception,
)
@attr.s(hash=True)
class NoValidSchema(OpenAPISchemaError):
value = attr.ib()
@ -33,20 +54,13 @@ class InvalidSchemaValue(OpenAPISchemaError):
return self.msg.format(value=self.value, type=self.type)
@attr.s(hash=True)
class InvalidCustomFormatSchemaValue(InvalidSchemaValue):
original_exception = attr.ib()
def __str__(self):
return self.msg.format(value=self.value, type=self.type, exception=self.original_exception)
@attr.s(hash=True)
class UndefinedSchemaProperty(OpenAPISchemaError):
extra_props = attr.ib()
def __str__(self):
return "Extra unexpected properties found in schema: {0}".format(self.extra_props)
return "Extra unexpected properties found in schema: {0}".format(
self.extra_props)
@attr.s(hash=True)
@ -55,7 +69,8 @@ class InvalidSchemaProperty(OpenAPISchemaError):
original_exception = attr.ib()
def __str__(self):
return "Invalid schema property {0}: {1}".format(self.property_name, self.original_exception)
return "Invalid schema property {0}: {1}".format(
self.property_name, self.original_exception)
@attr.s(hash=True)
@ -66,14 +81,46 @@ class MissingSchemaProperty(OpenAPISchemaError):
return "Missing schema property: {0}".format(self.property_name)
class UnmarshallerError(OpenAPIMappingError):
class UnmarshallerError(UnmarshallError):
"""Unmarshaller error"""
pass
@attr.s(hash=True)
class InvalidCustomFormatSchemaValue(UnmarshallerError):
"""Value failed to format with custom formatter"""
value = attr.ib()
type = attr.ib()
original_exception = attr.ib()
def __str__(self):
return (
"Failed to format value {value} to format {type}: {exception}"
).format(
value=self.value, type=self.type,
exception=self.original_exception,
)
@attr.s(hash=True)
class FormatterNotFoundError(UnmarshallerError):
"""Formatter not found to unmarshal"""
value = attr.ib()
type_format = attr.ib()
def __str__(self):
return (
"Formatter not found for {format} format "
"to unmarshal value {value}"
).format(format=self.type_format, value=self.value)
@attr.s(hash=True)
class UnmarshallerStrictTypeError(UnmarshallerError):
value = attr.ib()
types = attr.ib()
def __str__(self):
return "Value {value} is not one of types {types}".format(
self.value, self.types)
types = ', '.join(list(map(str, self.types)))
return "Value {value} is not one of types: {types}".format(
value=self.value, types=types)

View file

@ -17,8 +17,8 @@ from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
from openapi_core.schema.schemas.exceptions import (
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
OpenAPISchemaError, NoValidSchema,
UndefinedItemsSchema, InvalidCustomFormatSchemaValue, InvalidSchemaProperty,
UnmarshallerStrictTypeError,
UndefinedItemsSchema, InvalidSchemaProperty,
UnmarshallerError, UnmarshallValueError, UnmarshallError,
)
from openapi_core.schema.schemas.util import (
forcebool, format_date, format_datetime, format_byte, format_uuid,
@ -212,12 +212,12 @@ class Schema(object):
warnings.warn("The schema is deprecated", DeprecationWarning)
if value is None:
if not self.nullable:
raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type)
raise UnmarshallError(
"Null value for non-nullable schema", value, self.type)
return self.default
if self.enum and value not in self.enum:
raise InvalidSchemaValue(
"Value {value} not in enum choices: {type}", value, self.enum)
raise UnmarshallError("Invalid value for enum: {0}".format(value))
unmarshal_mapping = self.get_unmarshal_mapping(
custom_formatters=custom_formatters, strict=strict)
@ -228,12 +228,8 @@ class Schema(object):
unmarshal_callable = unmarshal_mapping[self.type]
try:
unmarshalled = unmarshal_callable(value)
except UnmarshallerStrictTypeError:
raise InvalidSchemaValue(
"Value {value} is not of type {type}", value, self.type)
except ValueError:
raise InvalidSchemaValue(
"Failed to unmarshal value {value} to type {type}", value, self.type)
except ValueError as exc:
raise UnmarshallValueError(value, self.type, exc)
return unmarshalled
@ -268,7 +264,7 @@ class Schema(object):
for subschema in self.one_of:
try:
unmarshalled = subschema.unmarshal(value, custom_formatters)
except (OpenAPISchemaError, TypeError, ValueError):
except UnmarshallError:
continue
else:
if result is not None:
@ -285,9 +281,7 @@ class Schema(object):
unmarshal_callable = unmarshal_mapping[schema_type]
try:
return unmarshal_callable(value)
except UnmarshallerStrictTypeError:
continue
except (OpenAPISchemaError, TypeError):
except (UnmarshallError, ValueError):
continue
log.warning("failed to unmarshal any type")
@ -295,7 +289,7 @@ class Schema(object):
def _unmarshal_collection(self, value, custom_formatters=None, strict=True):
if not isinstance(value, (list, tuple)):
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
raise ValueError("Invalid value for collection: {0}".format(value))
f = functools.partial(
self.items.unmarshal,
@ -306,7 +300,7 @@ class Schema(object):
def _unmarshal_object(self, value, model_factory=None,
custom_formatters=None, strict=True):
if not isinstance(value, (dict, )):
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
raise ValueError("Invalid value for object: {0}".format(value))
model_factory = model_factory or ModelFactory()

View file

@ -6,6 +6,7 @@ from openapi_core.schema.schemas.exceptions import (
OpenAPISchemaError,
InvalidSchemaProperty,
UnmarshallerStrictTypeError,
FormatterNotFoundError,
)
from openapi_core.schema.schemas.util import (
forcebool, format_date, format_datetime, format_byte, format_uuid,
@ -49,17 +50,12 @@ class PrimitiveTypeUnmarshaller(StrictUnmarshaller):
formatter = formatters.get(schema_format)
if formatter is None:
raise InvalidSchemaValue(
"Unsupported format {type} unmarshalling "
"for value {value}",
value, type_format)
raise FormatterNotFoundError(value, type_format)
try:
return formatter(value)
except ValueError as exc:
raise InvalidCustomFormatSchemaValue(
"Failed to format value {value} to format {type}: {exception}",
value, type_format, exc)
raise InvalidCustomFormatSchemaValue(value, type_format, exc)
def get_formatters(self):
return self.FORMATTERS

View file

@ -7,7 +7,9 @@ import pytest
from openapi_core.extensions.models.models import Model
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
from openapi_core.schema.schemas.exceptions import (
InvalidSchemaValue, OpenAPISchemaError,
InvalidSchemaValue, OpenAPISchemaError, UnmarshallerStrictTypeError,
UnmarshallValueError, UnmarshallError, InvalidCustomFormatSchemaValue,
FormatterNotFoundError,
)
from openapi_core.schema.schemas.models import Schema
@ -88,14 +90,14 @@ class TestSchemaUnmarshal(object):
schema = Schema('string')
value = 1.23
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallerStrictTypeError):
schema.unmarshal(value)
def test_string_none(self):
schema = Schema('string')
value = None
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallError):
schema.unmarshal(value)
def test_string_default(self):
@ -103,7 +105,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('string', default=default_value)
value = None
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallError):
schema.unmarshal(value)
def test_string_default_nullable(self):
@ -150,7 +152,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('string', schema_format=custom_format)
value = 'x'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(InvalidCustomFormatSchemaValue):
schema.unmarshal(
value, custom_formatters={custom_format: custom_formatter})
@ -168,7 +170,10 @@ class TestSchemaUnmarshal(object):
value = 'x'
with pytest.raises(
InvalidSchemaValue, message='Failed to format value'
FormatterNotFoundError,
message=(
'Formatter not found for custom format to unmarshal value x'
),
):
schema.unmarshal(value)
@ -184,14 +189,14 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer')
value = '123'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallerStrictTypeError):
schema.unmarshal(value)
def test_integer_enum_invalid(self):
schema = Schema('integer', enum=[1, 2, 3])
value = '123'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallError):
schema.unmarshal(value)
def test_integer_enum(self):
@ -206,7 +211,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer', enum=[1, 2, 3])
value = '2'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallError):
schema.unmarshal(value)
def test_integer_default(self):
@ -214,7 +219,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer', default=default_value)
value = None
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallError):
schema.unmarshal(value)
def test_integer_default_nullable(self):
@ -230,7 +235,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer')
value = 'abc'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallerStrictTypeError):
schema.unmarshal(value)
def test_array_valid(self):
@ -245,14 +250,14 @@ class TestSchemaUnmarshal(object):
schema = Schema('array', items=Schema('string'))
value = '123'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallValueError):
schema.unmarshal(value)
def test_array_of_integer_string_invalid(self):
schema = Schema('array', items=Schema('integer'))
value = '123'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallValueError):
schema.unmarshal(value)
def test_boolean_valid(self):
@ -267,7 +272,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('boolean')
value = 'True'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallerStrictTypeError):
schema.unmarshal(value)
def test_number_valid(self):
@ -282,7 +287,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('number')
value = '1.23'
with pytest.raises(InvalidSchemaValue):
with pytest.raises(UnmarshallerStrictTypeError):
schema.unmarshal(value)
def test_number_int(self):