mirror of
https://github.com/correl/openapi-core.git
synced 2024-12-29 11:09:25 +00:00
Schema unmarshal exceptions refactor
This commit is contained in:
parent
fd99117278
commit
939cec94e7
4 changed files with 94 additions and 52 deletions
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue