mirror of
https://github.com/correl/openapi-core.git
synced 2024-12-28 19:19:23 +00:00
Merge pull request #183 from p1c2u/refactor/move-unmarshallers-to-subpackage
Move Unmarshallers to separate subpackage
This commit is contained in:
commit
ca63475826
23 changed files with 537 additions and 468 deletions
5
openapi_core/exceptions.py
Normal file
5
openapi_core/exceptions.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
"""OpenAPI core exceptions module"""
|
||||
|
||||
|
||||
class OpenAPIError(Exception):
|
||||
pass
|
|
@ -1,8 +1,5 @@
|
|||
"""OpenAPI core schema exceptions module"""
|
||||
|
||||
|
||||
class OpenAPIError(Exception):
|
||||
pass
|
||||
from openapi_core.exceptions import OpenAPIError
|
||||
|
||||
|
||||
class OpenAPIMappingError(OpenAPIError):
|
||||
|
|
0
openapi_core/schema/extensions/__init__.py
Normal file
0
openapi_core/schema/extensions/__init__.py
Normal file
16
openapi_core/schema/extensions/generators.py
Normal file
16
openapi_core/schema/extensions/generators.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""OpenAPI core extensions generators module"""
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.extensions.models import Extension
|
||||
|
||||
|
||||
class ExtensionsGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
def generate(self, item_spec):
|
||||
for field_name, value in iteritems(item_spec):
|
||||
if not field_name.startswith('x-'):
|
||||
continue
|
||||
yield field_name, Extension(field_name, value)
|
9
openapi_core/schema/extensions/models.py
Normal file
9
openapi_core/schema/extensions/models.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""OpenAPI core extensions models module"""
|
||||
|
||||
|
||||
class Extension(object):
|
||||
"""Represents an OpenAPI Extension."""
|
||||
|
||||
def __init__(self, field_name, value=None):
|
||||
self.field_name = field_name
|
||||
self.value = value
|
|
@ -5,8 +5,9 @@ from json import loads
|
|||
|
||||
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
CastError, ValidateError, UnmarshalError,
|
||||
CastError, ValidateError,
|
||||
)
|
||||
from openapi_core.unmarshalling.schemas.exceptions import UnmarshalError
|
||||
|
||||
|
||||
MEDIA_TYPE_DESERIALIZERS = {
|
||||
|
|
|
@ -11,8 +11,9 @@ from openapi_core.schema.parameters.exceptions import (
|
|||
)
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
CastError, ValidateError, UnmarshalError,
|
||||
CastError, ValidateError,
|
||||
)
|
||||
from openapi_core.unmarshalling.schemas.exceptions import UnmarshalError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -81,10 +82,7 @@ class Parameter(object):
|
|||
if self.required:
|
||||
raise MissingRequiredParameter(self.name)
|
||||
|
||||
if not self.schema or self.schema.default is None:
|
||||
raise MissingParameter(self.name)
|
||||
|
||||
return self.schema.default
|
||||
raise MissingParameter(self.name)
|
||||
|
||||
if self.aslist and self.explode:
|
||||
if hasattr(location, 'getall'):
|
||||
|
|
|
@ -23,27 +23,6 @@ class ValidateError(OpenAPISchemaError):
|
|||
pass
|
||||
|
||||
|
||||
class UnmarshalError(OpenAPISchemaError):
|
||||
"""Schema unmarshal operation error"""
|
||||
pass
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class UnmarshalValueError(UnmarshalError):
|
||||
"""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 InvalidSchemaValue(ValidateError):
|
||||
value = attr.ib()
|
||||
|
@ -61,48 +40,3 @@ class InvalidSchemaValue(ValidateError):
|
|||
return (
|
||||
"Value {value} not valid for schema of type {type}: {errors}"
|
||||
).format(value=self.value, type=self.type, errors=self.schema_errors)
|
||||
|
||||
|
||||
class UnmarshallerError(UnmarshalError):
|
||||
"""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):
|
||||
types = ', '.join(list(map(str, self.types)))
|
||||
return "Value {value} is not one of types: {types}".format(
|
||||
value=self.value, types=types)
|
||||
|
|
|
@ -4,9 +4,10 @@ import logging
|
|||
from six import iteritems
|
||||
|
||||
from openapi_core.compat import lru_cache
|
||||
from openapi_core.schema.extensions.generators import ExtensionsGenerator
|
||||
from openapi_core.schema.properties.generators import PropertiesGenerator
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.schemas.types import Contribution
|
||||
from openapi_core.schema.schemas.types import Contribution, NoValue
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,9 +22,8 @@ class SchemaFactory(object):
|
|||
|
||||
schema_type = schema_deref.get('type', None)
|
||||
schema_format = schema_deref.get('format')
|
||||
model = schema_deref.get('x-model', None)
|
||||
required = schema_deref.get('required', False)
|
||||
default = schema_deref.get('default', None)
|
||||
default = schema_deref.get('default', NoValue)
|
||||
properties_spec = schema_deref.get('properties', None)
|
||||
items_spec = schema_deref.get('items', None)
|
||||
nullable = schema_deref.get('nullable', False)
|
||||
|
@ -47,6 +47,8 @@ class SchemaFactory(object):
|
|||
min_properties = schema_deref.get('minProperties', None)
|
||||
max_properties = schema_deref.get('maxProperties', None)
|
||||
|
||||
extensions = self.extensions_generator.generate(schema_deref)
|
||||
|
||||
properties = None
|
||||
if properties_spec:
|
||||
properties = self.properties_generator.generate(properties_spec)
|
||||
|
@ -68,7 +70,7 @@ class SchemaFactory(object):
|
|||
additional_properties = self.create(additional_properties_spec)
|
||||
|
||||
return Schema(
|
||||
schema_type=schema_type, model=model, properties=properties,
|
||||
schema_type=schema_type, properties=properties,
|
||||
items=items, schema_format=schema_format, required=required,
|
||||
default=default, nullable=nullable, enum=enum,
|
||||
deprecated=deprecated, all_of=all_of, one_of=one_of,
|
||||
|
@ -79,9 +81,15 @@ class SchemaFactory(object):
|
|||
exclusive_maximum=exclusive_maximum,
|
||||
exclusive_minimum=exclusive_minimum,
|
||||
min_properties=min_properties, max_properties=max_properties,
|
||||
extensions=extensions,
|
||||
_source=schema_deref,
|
||||
)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def extensions_generator(self):
|
||||
return ExtensionsGenerator(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def properties_generator(self):
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
"""OpenAPI core schemas models module"""
|
||||
import attr
|
||||
import functools
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from six import iteritems
|
||||
from jsonschema.exceptions import ValidationError
|
||||
|
||||
from openapi_core.extensions.models.factories import ModelFactory
|
||||
from openapi_core.schema.schemas._format import oas30_format_checker
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
CastError, InvalidSchemaValue,
|
||||
UnmarshalValueError, UnmarshalError,
|
||||
)
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
from openapi_core.schema.schemas.util import forcebool
|
||||
from openapi_core.schema.schemas.validators import OAS30Validator
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
UnmarshalValueError,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -37,20 +36,17 @@ class Schema(object):
|
|||
SchemaType.BOOLEAN: forcebool,
|
||||
}
|
||||
|
||||
DEFAULT_UNMARSHAL_CALLABLE_GETTER = {
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self, schema_type=None, model=None, properties=None, items=None,
|
||||
schema_format=None, required=None, default=None, nullable=False,
|
||||
self, schema_type=None, properties=None, items=None,
|
||||
schema_format=None, required=None, default=NoValue, nullable=False,
|
||||
enum=None, deprecated=False, all_of=None, one_of=None,
|
||||
additional_properties=True, min_items=None, max_items=None,
|
||||
min_length=None, max_length=None, pattern=None, unique_items=False,
|
||||
minimum=None, maximum=None, multiple_of=None,
|
||||
exclusive_minimum=False, exclusive_maximum=False,
|
||||
min_properties=None, max_properties=None, _source=None):
|
||||
min_properties=None, max_properties=None, extensions=None,
|
||||
_source=None):
|
||||
self.type = SchemaType(schema_type)
|
||||
self.model = model
|
||||
self.properties = properties and dict(properties) or {}
|
||||
self.items = items
|
||||
self.format = schema_format
|
||||
|
@ -79,6 +75,8 @@ class Schema(object):
|
|||
self.max_properties = int(max_properties)\
|
||||
if max_properties is not None else None
|
||||
|
||||
self.extensions = extensions and dict(extensions) or {}
|
||||
|
||||
self._all_required_properties_cache = None
|
||||
self._all_optional_properties_cache = None
|
||||
|
||||
|
@ -95,6 +93,9 @@ class Schema(object):
|
|||
def __getitem__(self, name):
|
||||
return self.properties[name]
|
||||
|
||||
def has_default(self):
|
||||
return self.default is not NoValue
|
||||
|
||||
def get_all_properties(self):
|
||||
properties = self.properties.copy()
|
||||
|
||||
|
@ -108,32 +109,6 @@ class Schema(object):
|
|||
all_properties = self.get_all_properties()
|
||||
return set(all_properties.keys())
|
||||
|
||||
def get_all_required_properties(self):
|
||||
if self._all_required_properties_cache is None:
|
||||
self._all_required_properties_cache =\
|
||||
self._get_all_required_properties()
|
||||
|
||||
return self._all_required_properties_cache
|
||||
|
||||
def _get_all_required_properties(self):
|
||||
all_properties = self.get_all_properties()
|
||||
required = self.get_all_required_properties_names()
|
||||
|
||||
return dict(
|
||||
(prop_name, val)
|
||||
for prop_name, val in iteritems(all_properties)
|
||||
if prop_name in required
|
||||
)
|
||||
|
||||
def get_all_required_properties_names(self):
|
||||
required = self.required[:]
|
||||
|
||||
for subschema in self.all_of:
|
||||
subschema_req = subschema.get_all_required_properties()
|
||||
required += subschema_req
|
||||
|
||||
return set(required)
|
||||
|
||||
def get_cast_mapping(self):
|
||||
mapping = self.TYPE_CAST_CALLABLE_GETTER.copy()
|
||||
mapping.update({
|
||||
|
@ -144,7 +119,7 @@ class Schema(object):
|
|||
|
||||
def cast(self, value):
|
||||
"""Cast value from string to schema type"""
|
||||
if value is None:
|
||||
if value in (None, NoValue):
|
||||
return value
|
||||
|
||||
cast_mapping = self.get_cast_mapping()
|
||||
|
@ -158,28 +133,6 @@ class Schema(object):
|
|||
def _cast_collection(self, value):
|
||||
return list(map(self.items.cast, value))
|
||||
|
||||
def get_unmarshal_mapping(self, custom_formatters=None, strict=True):
|
||||
primitive_unmarshallers = self.get_primitive_unmarshallers(
|
||||
custom_formatters=custom_formatters)
|
||||
|
||||
primitive_unmarshallers_partial = dict(
|
||||
(t, functools.partial(u, type_format=self.format, strict=strict))
|
||||
for t, u in primitive_unmarshallers.items()
|
||||
)
|
||||
|
||||
def pass_defaults(f):
|
||||
return functools.partial(
|
||||
f, custom_formatters=custom_formatters, strict=strict)
|
||||
mapping = self.DEFAULT_UNMARSHAL_CALLABLE_GETTER.copy()
|
||||
mapping.update(primitive_unmarshallers_partial)
|
||||
mapping.update({
|
||||
SchemaType.ANY: pass_defaults(self._unmarshal_any),
|
||||
SchemaType.ARRAY: pass_defaults(self._unmarshal_collection),
|
||||
SchemaType.OBJECT: pass_defaults(self._unmarshal_object),
|
||||
})
|
||||
|
||||
return defaultdict(lambda: lambda x: x, mapping)
|
||||
|
||||
def get_validator(self, resolver=None):
|
||||
return OAS30Validator(
|
||||
self.__dict__,
|
||||
|
@ -197,162 +150,13 @@ class Schema(object):
|
|||
|
||||
def unmarshal(self, value, custom_formatters=None, strict=True):
|
||||
"""Unmarshal parameter from the value."""
|
||||
if self.deprecated:
|
||||
warnings.warn("The schema is deprecated", DeprecationWarning)
|
||||
if value is None:
|
||||
if not self.nullable:
|
||||
raise UnmarshalError(
|
||||
"Null value for non-nullable schema", value, self.type)
|
||||
return self.default
|
||||
|
||||
if self.enum and value not in self.enum:
|
||||
raise UnmarshalError("Invalid value for enum: {0}".format(value))
|
||||
|
||||
unmarshal_mapping = self.get_unmarshal_mapping(
|
||||
custom_formatters=custom_formatters, strict=strict)
|
||||
|
||||
if self.type is not SchemaType.STRING and value == '':
|
||||
return None
|
||||
|
||||
unmarshal_callable = unmarshal_mapping[self.type]
|
||||
from openapi_core.unmarshalling.schemas.factories import (
|
||||
SchemaUnmarshallersFactory,
|
||||
)
|
||||
unmarshallers_factory = SchemaUnmarshallersFactory(
|
||||
custom_formatters)
|
||||
unmarshaller = unmarshallers_factory.create(self)
|
||||
try:
|
||||
unmarshalled = unmarshal_callable(value)
|
||||
return unmarshaller(value, strict=strict)
|
||||
except ValueError as exc:
|
||||
raise UnmarshalValueError(value, self.type, exc)
|
||||
|
||||
return unmarshalled
|
||||
|
||||
def get_primitive_unmarshallers(self, **options):
|
||||
from openapi_core.schema.schemas.unmarshallers import (
|
||||
StringUnmarshaller, BooleanUnmarshaller, IntegerUnmarshaller,
|
||||
NumberUnmarshaller,
|
||||
)
|
||||
|
||||
unmarshallers_classes = {
|
||||
SchemaType.STRING: StringUnmarshaller,
|
||||
SchemaType.BOOLEAN: BooleanUnmarshaller,
|
||||
SchemaType.INTEGER: IntegerUnmarshaller,
|
||||
SchemaType.NUMBER: NumberUnmarshaller,
|
||||
}
|
||||
|
||||
unmarshallers = dict(
|
||||
(t, klass(**options))
|
||||
for t, klass in unmarshallers_classes.items()
|
||||
)
|
||||
|
||||
return unmarshallers
|
||||
|
||||
def _unmarshal_any(self, value, custom_formatters=None, strict=True):
|
||||
types_resolve_order = [
|
||||
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
|
||||
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
|
||||
]
|
||||
unmarshal_mapping = self.get_unmarshal_mapping()
|
||||
if self.one_of:
|
||||
result = None
|
||||
for subschema in self.one_of:
|
||||
try:
|
||||
unmarshalled = subschema.unmarshal(
|
||||
value, custom_formatters)
|
||||
except UnmarshalError:
|
||||
continue
|
||||
else:
|
||||
if result is not None:
|
||||
log.warning("multiple valid oneOf schemas found")
|
||||
continue
|
||||
result = unmarshalled
|
||||
|
||||
if result is None:
|
||||
log.warning("valid oneOf schema not found")
|
||||
|
||||
return result
|
||||
else:
|
||||
for schema_type in types_resolve_order:
|
||||
unmarshal_callable = unmarshal_mapping[schema_type]
|
||||
try:
|
||||
return unmarshal_callable(value)
|
||||
except (UnmarshalError, ValueError):
|
||||
continue
|
||||
|
||||
log.warning("failed to unmarshal any type")
|
||||
return value
|
||||
|
||||
def _unmarshal_collection(
|
||||
self, value, custom_formatters=None, strict=True):
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise ValueError(
|
||||
"Invalid value for collection: {0}".format(value))
|
||||
|
||||
f = functools.partial(
|
||||
self.items.unmarshal,
|
||||
custom_formatters=custom_formatters, strict=strict,
|
||||
)
|
||||
return list(map(f, value))
|
||||
|
||||
def _unmarshal_object(self, value, model_factory=None,
|
||||
custom_formatters=None, strict=True):
|
||||
if not isinstance(value, (dict, )):
|
||||
raise ValueError("Invalid value for object: {0}".format(value))
|
||||
|
||||
model_factory = model_factory or ModelFactory()
|
||||
|
||||
if self.one_of:
|
||||
properties = None
|
||||
for one_of_schema in self.one_of:
|
||||
try:
|
||||
unmarshalled = self._unmarshal_properties(
|
||||
value, one_of_schema,
|
||||
custom_formatters=custom_formatters,
|
||||
)
|
||||
except (UnmarshalError, ValueError):
|
||||
pass
|
||||
else:
|
||||
if properties is not None:
|
||||
log.warning("multiple valid oneOf schemas found")
|
||||
continue
|
||||
properties = unmarshalled
|
||||
|
||||
if properties is None:
|
||||
log.warning("valid oneOf schema not found")
|
||||
|
||||
else:
|
||||
properties = self._unmarshal_properties(
|
||||
value, custom_formatters=custom_formatters)
|
||||
|
||||
return model_factory.create(properties, name=self.model)
|
||||
|
||||
def _unmarshal_properties(self, value, one_of_schema=None,
|
||||
custom_formatters=None, strict=True):
|
||||
all_props = self.get_all_properties()
|
||||
all_props_names = self.get_all_properties_names()
|
||||
all_req_props_names = self.get_all_required_properties_names()
|
||||
|
||||
if one_of_schema is not None:
|
||||
all_props.update(one_of_schema.get_all_properties())
|
||||
all_props_names |= one_of_schema.\
|
||||
get_all_properties_names()
|
||||
all_req_props_names |= one_of_schema.\
|
||||
get_all_required_properties_names()
|
||||
|
||||
value_props_names = value.keys()
|
||||
extra_props = set(value_props_names) - set(all_props_names)
|
||||
|
||||
properties = {}
|
||||
if self.additional_properties is not True:
|
||||
for prop_name in extra_props:
|
||||
prop_value = value[prop_name]
|
||||
properties[prop_name] = self.additional_properties.unmarshal(
|
||||
prop_value, custom_formatters=custom_formatters)
|
||||
|
||||
for prop_name, prop in iteritems(all_props):
|
||||
try:
|
||||
prop_value = value[prop_name]
|
||||
except KeyError:
|
||||
if not prop.nullable and not prop.default:
|
||||
continue
|
||||
prop_value = prop.default
|
||||
|
||||
properties[prop_name] = prop.unmarshal(
|
||||
prop_value, custom_formatters=custom_formatters)
|
||||
|
||||
return properties
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import attr
|
||||
|
||||
|
||||
NoValue = object()
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class Contribution(object):
|
||||
src_prop_name = attr.ib()
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
from six import text_type, binary_type, integer_types
|
||||
|
||||
from openapi_core.schema.schemas.enums import SchemaFormat
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
InvalidCustomFormatSchemaValue,
|
||||
UnmarshallerStrictTypeError,
|
||||
FormatterNotFoundError,
|
||||
)
|
||||
from openapi_core.schema.schemas.util import (
|
||||
forcebool, format_date, format_datetime, format_byte, format_uuid,
|
||||
format_number,
|
||||
)
|
||||
|
||||
|
||||
class StrictUnmarshaller(object):
|
||||
|
||||
STRICT_TYPES = ()
|
||||
|
||||
def __call__(self, value, type_format=SchemaFormat.NONE, strict=True):
|
||||
if self.STRICT_TYPES and strict and not isinstance(
|
||||
value, self.STRICT_TYPES):
|
||||
raise UnmarshallerStrictTypeError(value, self.STRICT_TYPES)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class PrimitiveTypeUnmarshaller(StrictUnmarshaller):
|
||||
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: lambda x: x,
|
||||
}
|
||||
|
||||
def __init__(self, custom_formatters=None):
|
||||
if custom_formatters is None:
|
||||
custom_formatters = {}
|
||||
self.custom_formatters = custom_formatters
|
||||
|
||||
def __call__(self, value, type_format=SchemaFormat.NONE, strict=True):
|
||||
value = super(PrimitiveTypeUnmarshaller, self).__call__(
|
||||
value, type_format=type_format, strict=strict)
|
||||
|
||||
try:
|
||||
schema_format = SchemaFormat(type_format)
|
||||
except ValueError:
|
||||
formatter = self.custom_formatters.get(type_format)
|
||||
else:
|
||||
formatters = self.get_formatters()
|
||||
formatter = formatters.get(schema_format)
|
||||
|
||||
if formatter is None:
|
||||
raise FormatterNotFoundError(value, type_format)
|
||||
|
||||
try:
|
||||
return formatter(value)
|
||||
except ValueError as exc:
|
||||
raise InvalidCustomFormatSchemaValue(value, type_format, exc)
|
||||
|
||||
def get_formatters(self):
|
||||
return self.FORMATTERS
|
||||
|
||||
|
||||
class StringUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (text_type, binary_type)
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: text_type,
|
||||
SchemaFormat.PASSWORD: text_type,
|
||||
SchemaFormat.DATE: format_date,
|
||||
SchemaFormat.DATETIME: format_datetime,
|
||||
SchemaFormat.BINARY: binary_type,
|
||||
SchemaFormat.UUID: format_uuid,
|
||||
SchemaFormat.BYTE: format_byte,
|
||||
}
|
||||
|
||||
|
||||
class IntegerUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = integer_types
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: int,
|
||||
SchemaFormat.INT32: int,
|
||||
SchemaFormat.INT64: int,
|
||||
}
|
||||
|
||||
|
||||
class NumberUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (float, ) + integer_types
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: format_number,
|
||||
SchemaFormat.FLOAT: float,
|
||||
SchemaFormat.DOUBLE: float,
|
||||
}
|
||||
|
||||
|
||||
class BooleanUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (bool, )
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: forcebool,
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
"""OpenAPI core schemas util module"""
|
||||
from base64 import b64decode
|
||||
import datetime
|
||||
from distutils.util import strtobool
|
||||
from six import string_types
|
||||
from json import dumps
|
||||
from six import string_types, text_type, integer_types
|
||||
import strict_rfc3339
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
def forcebool(val):
|
||||
|
@ -17,29 +13,3 @@ def forcebool(val):
|
|||
|
||||
def dicthash(d):
|
||||
return hash(dumps(d, sort_keys=True))
|
||||
|
||||
|
||||
def format_date(value):
|
||||
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
|
||||
|
||||
|
||||
def format_datetime(value):
|
||||
timestamp = strict_rfc3339.rfc3339_to_timestamp(value)
|
||||
return datetime.datetime.utcfromtimestamp(timestamp)
|
||||
|
||||
|
||||
def format_uuid(value):
|
||||
if isinstance(value, UUID):
|
||||
return value
|
||||
return UUID(value)
|
||||
|
||||
|
||||
def format_byte(value, encoding='utf8'):
|
||||
return text_type(b64decode(value), encoding)
|
||||
|
||||
|
||||
def format_number(value):
|
||||
if isinstance(value, integer_types + (float, )):
|
||||
return value
|
||||
|
||||
return float(value)
|
||||
|
|
0
openapi_core/unmarshalling/__init__.py
Normal file
0
openapi_core/unmarshalling/__init__.py
Normal file
0
openapi_core/unmarshalling/schemas/__init__.py
Normal file
0
openapi_core/unmarshalling/schemas/__init__.py
Normal file
66
openapi_core/unmarshalling/schemas/exceptions.py
Normal file
66
openapi_core/unmarshalling/schemas/exceptions.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import attr
|
||||
|
||||
from openapi_core.exceptions import OpenAPIError
|
||||
|
||||
|
||||
class UnmarshalError(OpenAPIError):
|
||||
"""Schema unmarshal operation error"""
|
||||
pass
|
||||
|
||||
|
||||
class UnmarshallerError(UnmarshalError):
|
||||
"""Unmarshaller error"""
|
||||
pass
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class UnmarshalValueError(UnmarshalError):
|
||||
"""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 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"""
|
||||
type_format = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
return "Formatter not found for {format} format".format(
|
||||
format=self.type_format)
|
||||
|
||||
|
||||
@attr.s(hash=True)
|
||||
class UnmarshallerStrictTypeError(UnmarshallerError):
|
||||
value = attr.ib()
|
||||
types = attr.ib()
|
||||
|
||||
def __str__(self):
|
||||
types = ', '.join(list(map(str, self.types)))
|
||||
return "Value {value} is not one of types: {types}".format(
|
||||
value=self.value, types=types)
|
62
openapi_core/unmarshalling/schemas/factories.py
Normal file
62
openapi_core/unmarshalling/schemas/factories.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import warnings
|
||||
|
||||
from openapi_core.schema.schemas.enums import SchemaType, SchemaFormat
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
FormatterNotFoundError,
|
||||
)
|
||||
from openapi_core.unmarshalling.schemas.unmarshallers import (
|
||||
StringUnmarshaller, IntegerUnmarshaller, NumberUnmarshaller,
|
||||
BooleanUnmarshaller, ArrayUnmarshaller, ObjectUnmarshaller,
|
||||
AnyUnmarshaller,
|
||||
)
|
||||
|
||||
|
||||
class SchemaUnmarshallersFactory(object):
|
||||
|
||||
PRIMITIVE_UNMARSHALLERS = {
|
||||
SchemaType.STRING: StringUnmarshaller,
|
||||
SchemaType.INTEGER: IntegerUnmarshaller,
|
||||
SchemaType.NUMBER: NumberUnmarshaller,
|
||||
SchemaType.BOOLEAN: BooleanUnmarshaller,
|
||||
}
|
||||
COMPLEX_UNMARSHALLERS = {
|
||||
SchemaType.ARRAY: ArrayUnmarshaller,
|
||||
SchemaType.OBJECT: ObjectUnmarshaller,
|
||||
SchemaType.ANY: AnyUnmarshaller,
|
||||
}
|
||||
|
||||
def __init__(self, custom_formatters=None):
|
||||
if custom_formatters is None:
|
||||
custom_formatters = {}
|
||||
self.custom_formatters = custom_formatters
|
||||
|
||||
def create(self, schema, type_override=None):
|
||||
"""Create unmarshaller from the schema."""
|
||||
if schema.deprecated:
|
||||
warnings.warn("The schema is deprecated", DeprecationWarning)
|
||||
|
||||
schema_type = type_override or schema.type
|
||||
if schema_type in self.PRIMITIVE_UNMARSHALLERS:
|
||||
klass = self.PRIMITIVE_UNMARSHALLERS[schema_type]
|
||||
kwargs = dict(schema=schema)
|
||||
|
||||
elif schema_type in self.COMPLEX_UNMARSHALLERS:
|
||||
klass = self.COMPLEX_UNMARSHALLERS[schema_type]
|
||||
kwargs = dict(schema=schema, unmarshallers_factory=self)
|
||||
|
||||
formatter = self.get_formatter(klass.FORMATTERS, schema.format)
|
||||
|
||||
if formatter is None:
|
||||
raise FormatterNotFoundError(schema.format)
|
||||
|
||||
return klass(formatter, **kwargs)
|
||||
|
||||
def get_formatter(self, formatters, type_format=SchemaFormat.NONE):
|
||||
try:
|
||||
schema_format = SchemaFormat(type_format)
|
||||
except ValueError:
|
||||
return self.custom_formatters.get(type_format)
|
||||
else:
|
||||
if schema_format == SchemaFormat.NONE:
|
||||
return lambda x: x
|
||||
return formatters.get(schema_format)
|
240
openapi_core/unmarshalling/schemas/unmarshallers.py
Normal file
240
openapi_core/unmarshalling/schemas/unmarshallers.py
Normal file
|
@ -0,0 +1,240 @@
|
|||
import logging
|
||||
|
||||
from six import text_type, binary_type, integer_types
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.extensions.models.factories import ModelFactory
|
||||
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
ValidateError,
|
||||
)
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
UnmarshalError,
|
||||
InvalidCustomFormatSchemaValue,
|
||||
UnmarshallerStrictTypeError,
|
||||
)
|
||||
from openapi_core.unmarshalling.schemas.util import (
|
||||
forcebool, format_date, format_datetime, format_byte, format_uuid,
|
||||
format_number,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StrictUnmarshaller(object):
|
||||
|
||||
STRICT_TYPES = ()
|
||||
|
||||
def __call__(self, value, strict=True):
|
||||
if strict and not self._is_strict(value):
|
||||
raise UnmarshallerStrictTypeError(value, self.STRICT_TYPES)
|
||||
|
||||
return value
|
||||
|
||||
def _is_strict(self, value):
|
||||
if not self.STRICT_TYPES:
|
||||
return True
|
||||
|
||||
return isinstance(value, self.STRICT_TYPES)
|
||||
|
||||
|
||||
class PrimitiveTypeUnmarshaller(StrictUnmarshaller):
|
||||
|
||||
FORMATTERS = {}
|
||||
|
||||
def __init__(self, formatter, schema):
|
||||
self.formatter = formatter
|
||||
self.schema = schema
|
||||
|
||||
def __call__(self, value=NoValue, strict=True):
|
||||
if value is NoValue:
|
||||
value = self.schema.default
|
||||
if value is None:
|
||||
return
|
||||
value = super(PrimitiveTypeUnmarshaller, self).__call__(
|
||||
value, strict=strict)
|
||||
|
||||
return self.format(value)
|
||||
|
||||
def format(self, value):
|
||||
try:
|
||||
return self.formatter(value)
|
||||
except ValueError as exc:
|
||||
raise InvalidCustomFormatSchemaValue(
|
||||
value, self.schema.format, exc)
|
||||
|
||||
|
||||
class StringUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (text_type, binary_type)
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: text_type,
|
||||
SchemaFormat.PASSWORD: text_type,
|
||||
SchemaFormat.DATE: format_date,
|
||||
SchemaFormat.DATETIME: format_datetime,
|
||||
SchemaFormat.BINARY: binary_type,
|
||||
SchemaFormat.UUID: format_uuid,
|
||||
SchemaFormat.BYTE: format_byte,
|
||||
}
|
||||
|
||||
|
||||
class IntegerUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = integer_types
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: int,
|
||||
SchemaFormat.INT32: int,
|
||||
SchemaFormat.INT64: int,
|
||||
}
|
||||
|
||||
|
||||
class NumberUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (float, ) + integer_types
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: format_number,
|
||||
SchemaFormat.FLOAT: float,
|
||||
SchemaFormat.DOUBLE: float,
|
||||
}
|
||||
|
||||
|
||||
class BooleanUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (bool, )
|
||||
FORMATTERS = {
|
||||
SchemaFormat.NONE: forcebool,
|
||||
}
|
||||
|
||||
|
||||
class ComplexUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
||||
def __init__(self, formatter, schema, unmarshallers_factory):
|
||||
super(ComplexUnmarshaller, self).__init__(formatter, schema)
|
||||
self.unmarshallers_factory = unmarshallers_factory
|
||||
|
||||
|
||||
class ArrayUnmarshaller(ComplexUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (list, tuple)
|
||||
FORMATTERS = {}
|
||||
|
||||
@property
|
||||
def items_unmarshaller(self):
|
||||
return self.unmarshallers_factory.create(self.schema.items)
|
||||
|
||||
def __call__(self, value=NoValue, strict=True):
|
||||
value = super(ArrayUnmarshaller, self).__call__(value, strict=strict)
|
||||
|
||||
self.unmarshallers_factory.create(self.schema.items)
|
||||
|
||||
return list(map(self.items_unmarshaller, value))
|
||||
|
||||
|
||||
class ObjectUnmarshaller(ComplexUnmarshaller):
|
||||
|
||||
STRICT_TYPES = (dict, )
|
||||
FORMATTERS = {}
|
||||
|
||||
@property
|
||||
def model_factory(self):
|
||||
return ModelFactory()
|
||||
|
||||
def __call__(self, value=NoValue, strict=True):
|
||||
value = super(ObjectUnmarshaller, self).__call__(value, strict=strict)
|
||||
|
||||
if self.schema.one_of:
|
||||
properties = None
|
||||
for one_of_schema in self.schema.one_of:
|
||||
try:
|
||||
unmarshalled = self._unmarshal_properties(
|
||||
value, one_of_schema, strict=strict)
|
||||
except (UnmarshalError, ValueError):
|
||||
pass
|
||||
else:
|
||||
if properties is not None:
|
||||
log.warning("multiple valid oneOf schemas found")
|
||||
continue
|
||||
properties = unmarshalled
|
||||
|
||||
if properties is None:
|
||||
log.warning("valid oneOf schema not found")
|
||||
|
||||
else:
|
||||
properties = self._unmarshal_properties(value)
|
||||
|
||||
if 'x-model' in self.schema.extensions:
|
||||
extension = self.schema.extensions['x-model']
|
||||
return self.model_factory.create(properties, name=extension.value)
|
||||
|
||||
return properties
|
||||
|
||||
def _unmarshal_properties(
|
||||
self, value=NoValue, one_of_schema=None, strict=True):
|
||||
all_props = self.schema.get_all_properties()
|
||||
all_props_names = self.schema.get_all_properties_names()
|
||||
|
||||
if one_of_schema is not None:
|
||||
all_props.update(one_of_schema.get_all_properties())
|
||||
all_props_names |= one_of_schema.\
|
||||
get_all_properties_names()
|
||||
|
||||
value_props_names = value.keys()
|
||||
extra_props = set(value_props_names) - set(all_props_names)
|
||||
|
||||
properties = {}
|
||||
if self.schema.additional_properties is not True:
|
||||
for prop_name in extra_props:
|
||||
prop_value = value[prop_name]
|
||||
properties[prop_name] = self.unmarshallers_factory.create(
|
||||
self.schema.additional_properties)(
|
||||
prop_value, strict=strict)
|
||||
|
||||
for prop_name, prop in iteritems(all_props):
|
||||
try:
|
||||
prop_value = value[prop_name]
|
||||
except KeyError:
|
||||
if prop.default is NoValue:
|
||||
continue
|
||||
prop_value = prop.default
|
||||
|
||||
properties[prop_name] = self.unmarshallers_factory.create(
|
||||
prop)(prop_value, strict=strict)
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
class AnyUnmarshaller(ComplexUnmarshaller):
|
||||
|
||||
SCHEMA_TYPES_ORDER = [
|
||||
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
|
||||
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
|
||||
]
|
||||
|
||||
def __call__(self, value=NoValue, strict=True):
|
||||
one_of_schema = self._get_one_of_schema(value)
|
||||
if one_of_schema:
|
||||
return self.unmarshallers_factory.create(one_of_schema)(
|
||||
value, strict=strict)
|
||||
|
||||
for schema_type in self.SCHEMA_TYPES_ORDER:
|
||||
try:
|
||||
unmarshaller = self.unmarshallers_factory.create(
|
||||
self.schema, type_override=schema_type)
|
||||
return unmarshaller(value, strict=strict)
|
||||
except (UnmarshalError, ValueError):
|
||||
continue
|
||||
|
||||
log.warning("failed to unmarshal any type")
|
||||
return value
|
||||
|
||||
def _get_one_of_schema(self, value):
|
||||
if not self.schema.one_of:
|
||||
return
|
||||
for subschema in self.schema.one_of:
|
||||
try:
|
||||
subschema.validate(value)
|
||||
except ValidateError:
|
||||
continue
|
||||
else:
|
||||
return subschema
|
40
openapi_core/unmarshalling/schemas/util.py
Normal file
40
openapi_core/unmarshalling/schemas/util.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
"""OpenAPI core schemas util module"""
|
||||
from base64 import b64decode
|
||||
import datetime
|
||||
from distutils.util import strtobool
|
||||
from six import string_types, text_type, integer_types
|
||||
import strict_rfc3339
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
def forcebool(val):
|
||||
if isinstance(val, string_types):
|
||||
val = strtobool(val)
|
||||
|
||||
return bool(val)
|
||||
|
||||
|
||||
def format_date(value):
|
||||
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
|
||||
|
||||
|
||||
def format_datetime(value):
|
||||
timestamp = strict_rfc3339.rfc3339_to_timestamp(value)
|
||||
return datetime.datetime.utcfromtimestamp(timestamp)
|
||||
|
||||
|
||||
def format_uuid(value):
|
||||
if isinstance(value, UUID):
|
||||
return value
|
||||
return UUID(value)
|
||||
|
||||
|
||||
def format_byte(value, encoding='utf8'):
|
||||
return text_type(b64decode(value), encoding)
|
||||
|
||||
|
||||
def format_number(value):
|
||||
if isinstance(value, integer_types + (float, )):
|
||||
return value
|
||||
|
||||
return float(value)
|
|
@ -10,6 +10,18 @@ from six.moves.urllib.parse import urljoin
|
|||
|
||||
@attr.s
|
||||
class RequestParameters(object):
|
||||
"""OpenAPI request parameters dataclass.
|
||||
|
||||
Attributes:
|
||||
path
|
||||
Path parameters as dict.
|
||||
query
|
||||
Query string parameters as MultiDict. Must support getlist method.
|
||||
header
|
||||
Request headers as dict.
|
||||
cookie
|
||||
Request cookies as dict.
|
||||
"""
|
||||
path = attr.ib(factory=dict)
|
||||
query = attr.ib(factory=ImmutableMultiDict)
|
||||
header = attr.ib(factory=dict)
|
||||
|
|
|
@ -7,7 +7,7 @@ from openapi_core.schema.media_types.exceptions import (
|
|||
)
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
OpenAPIParameterError, MissingRequiredParameter,
|
||||
OpenAPIParameterError, MissingRequiredParameter, MissingParameter,
|
||||
)
|
||||
from openapi_core.schema.paths.exceptions import InvalidPath
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
|
@ -74,14 +74,16 @@ class RequestValidator(object):
|
|||
except MissingRequiredParameter as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
except OpenAPIParameterError:
|
||||
continue
|
||||
|
||||
try:
|
||||
casted = param.cast(raw_value)
|
||||
except OpenAPIParameterError as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
except MissingParameter:
|
||||
if not param.schema or not param.schema.has_default():
|
||||
continue
|
||||
casted = param.schema.default
|
||||
else:
|
||||
try:
|
||||
casted = param.cast(raw_value)
|
||||
except OpenAPIParameterError as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
|
||||
try:
|
||||
unmarshalled = param.unmarshal(
|
||||
|
|
|
@ -278,6 +278,7 @@ components:
|
|||
$ref: "#/components/schemas/Pet"
|
||||
PetsData:
|
||||
type: object
|
||||
x-model: PetsData
|
||||
required:
|
||||
- data
|
||||
properties:
|
||||
|
@ -285,6 +286,7 @@ components:
|
|||
$ref: "#/components/schemas/Pets"
|
||||
PetData:
|
||||
type: object
|
||||
x-model: PetData
|
||||
required:
|
||||
- data
|
||||
properties:
|
||||
|
@ -292,6 +294,7 @@ components:
|
|||
$ref: "#/components/schemas/Pet"
|
||||
TagCreate:
|
||||
type: object
|
||||
x-model: TagCreate
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
|
@ -316,6 +319,7 @@ components:
|
|||
message:
|
||||
type: string
|
||||
ExtendedError:
|
||||
x-model: ExtendedError
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/Error"
|
||||
- type: object
|
||||
|
|
|
@ -7,11 +7,16 @@ 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, UnmarshallerStrictTypeError,
|
||||
UnmarshalValueError, UnmarshalError, InvalidCustomFormatSchemaValue,
|
||||
FormatterNotFoundError,
|
||||
InvalidSchemaValue, OpenAPISchemaError,
|
||||
)
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
InvalidCustomFormatSchemaValue,
|
||||
FormatterNotFoundError,
|
||||
UnmarshalError,
|
||||
UnmarshallerStrictTypeError,
|
||||
)
|
||||
|
||||
from six import b, u
|
||||
|
||||
|
@ -50,9 +55,8 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema(schema_type)
|
||||
value = ''
|
||||
|
||||
result = schema.unmarshal(value)
|
||||
|
||||
assert result is None
|
||||
with pytest.raises(UnmarshallerStrictTypeError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_valid(self):
|
||||
schema = Schema('string')
|
||||
|
@ -93,25 +97,19 @@ class TestSchemaUnmarshal(object):
|
|||
with pytest.raises(UnmarshallerStrictTypeError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_none(self):
|
||||
schema = Schema('string')
|
||||
value = None
|
||||
|
||||
with pytest.raises(UnmarshalError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_default(self):
|
||||
default_value = 'default'
|
||||
schema = Schema('string', default=default_value)
|
||||
value = None
|
||||
value = NoValue
|
||||
|
||||
with pytest.raises(UnmarshalError):
|
||||
schema.unmarshal(value)
|
||||
result = schema.unmarshal(value)
|
||||
|
||||
def test_string_default_nullable(self):
|
||||
default_value = 'default'
|
||||
assert result == default_value
|
||||
|
||||
@pytest.mark.parametrize('default_value', ['default', None])
|
||||
def test_string_default_nullable(self, default_value):
|
||||
schema = Schema('string', default=default_value, nullable=True)
|
||||
value = None
|
||||
value = NoValue
|
||||
|
||||
result = schema.unmarshal(value)
|
||||
|
||||
|
@ -161,7 +159,7 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('string', schema_format=unknown_format)
|
||||
value = 'x'
|
||||
|
||||
with pytest.raises(OpenAPISchemaError):
|
||||
with pytest.raises(FormatterNotFoundError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_format_invalid_value(self):
|
||||
|
@ -172,7 +170,7 @@ class TestSchemaUnmarshal(object):
|
|||
with pytest.raises(
|
||||
FormatterNotFoundError,
|
||||
message=(
|
||||
'Formatter not found for custom format to unmarshal value x'
|
||||
'Formatter not found for custom format'
|
||||
),
|
||||
):
|
||||
schema.unmarshal(value)
|
||||
|
@ -215,21 +213,22 @@ class TestSchemaUnmarshal(object):
|
|||
schema.unmarshal(value)
|
||||
|
||||
def test_integer_default(self):
|
||||
default_value = '123'
|
||||
default_value = 123
|
||||
schema = Schema('integer', default=default_value)
|
||||
value = None
|
||||
value = NoValue
|
||||
|
||||
with pytest.raises(UnmarshalError):
|
||||
schema.unmarshal(value)
|
||||
result = schema.unmarshal(value)
|
||||
|
||||
assert result == default_value
|
||||
|
||||
def test_integer_default_nullable(self):
|
||||
default_value = '123'
|
||||
default_value = 123
|
||||
schema = Schema('integer', default=default_value, nullable=True)
|
||||
value = None
|
||||
|
||||
result = schema.unmarshal(value)
|
||||
|
||||
assert result == default_value
|
||||
assert result is None
|
||||
|
||||
def test_integer_invalid(self):
|
||||
schema = Schema('integer')
|
||||
|
@ -250,14 +249,14 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('array', items=Schema('string'))
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(UnmarshalValueError):
|
||||
with pytest.raises(UnmarshallerStrictTypeError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_array_of_integer_string_invalid(self):
|
||||
schema = Schema('array', items=Schema('integer'))
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(UnmarshalValueError):
|
||||
with pytest.raises(UnmarshallerStrictTypeError):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_boolean_valid(self):
|
||||
|
@ -702,7 +701,7 @@ class TestSchemaValidate(object):
|
|||
unknown_format = 'unknown'
|
||||
schema = Schema('string', schema_format=unknown_format)
|
||||
|
||||
with pytest.raises(OpenAPISchemaError):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.validate(value)
|
||||
|
||||
@pytest.mark.parametrize('value', [u(""), u("a"), u("ab")])
|
||||
|
|
Loading…
Reference in a new issue