From 368f6882e5f23e7a413b0fa5389a3e782ae4eed0 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Thu, 23 Jan 2020 22:48:34 +0000 Subject: [PATCH] Move casters to separate subpackage --- openapi_core/casting/__init__.py | 0 openapi_core/casting/schemas/__init__.py | 0 openapi_core/casting/schemas/casters.py | 34 ++++++++++++++++++++ openapi_core/casting/schemas/exceptions.py | 14 ++++++++ openapi_core/casting/schemas/factories.py | 29 +++++++++++++++++ openapi_core/casting/schemas/util.py | 10 ++++++ openapi_core/schema/media_types/models.py | 3 +- openapi_core/schema/parameters/models.py | 3 +- openapi_core/schema/schemas/exceptions.py | 11 ------- openapi_core/schema/schemas/models.py | 37 ++++------------------ 10 files changed, 98 insertions(+), 43 deletions(-) create mode 100644 openapi_core/casting/__init__.py create mode 100644 openapi_core/casting/schemas/__init__.py create mode 100644 openapi_core/casting/schemas/casters.py create mode 100644 openapi_core/casting/schemas/exceptions.py create mode 100644 openapi_core/casting/schemas/factories.py create mode 100644 openapi_core/casting/schemas/util.py diff --git a/openapi_core/casting/__init__.py b/openapi_core/casting/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi_core/casting/schemas/__init__.py b/openapi_core/casting/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openapi_core/casting/schemas/casters.py b/openapi_core/casting/schemas/casters.py new file mode 100644 index 0000000..4959fa6 --- /dev/null +++ b/openapi_core/casting/schemas/casters.py @@ -0,0 +1,34 @@ +from openapi_core.schema.schemas.types import NoValue + + +class PrimitiveCaster(object): + + def __init__(self, caster_callable): + self.caster_callable = caster_callable + + def __call__(self, value): + if value in (None, NoValue): + return value + return self.caster_callable(value) + + +class DummyCaster(object): + + def __call__(self, value): + return value + + +class ArrayCaster(object): + + def __init__(self, schema, casters_factory): + self.schema = schema + self.casters_factory = casters_factory + + @property + def items_caster(self): + return self.casters_factory.create(self.schema.items) + + def __call__(self, value): + if value in (None, NoValue): + return value + return list(map(self.items_caster, value)) diff --git a/openapi_core/casting/schemas/exceptions.py b/openapi_core/casting/schemas/exceptions.py new file mode 100644 index 0000000..cc10672 --- /dev/null +++ b/openapi_core/casting/schemas/exceptions.py @@ -0,0 +1,14 @@ +import attr + +from openapi_core.exceptions import OpenAPIError + + +@attr.s(hash=True) +class CastError(OpenAPIError): + """Schema cast operation error""" + value = attr.ib() + type = attr.ib() + + def __str__(self): + return "Failed to cast value {value} to type {type}".format( + value=self.value, type=self.type) diff --git a/openapi_core/casting/schemas/factories.py b/openapi_core/casting/schemas/factories.py new file mode 100644 index 0000000..f18cb84 --- /dev/null +++ b/openapi_core/casting/schemas/factories.py @@ -0,0 +1,29 @@ +from openapi_core.schema.schemas.enums import SchemaType + +from openapi_core.casting.schemas.casters import ( + PrimitiveCaster, DummyCaster, ArrayCaster +) +from openapi_core.casting.schemas.util import forcebool + + +class SchemaCastersFactory(object): + + DUMMY_CASTER = DummyCaster() + PRIMITIVE_CASTERS = { + SchemaType.STRING: DUMMY_CASTER, + SchemaType.INTEGER: PrimitiveCaster(int), + SchemaType.NUMBER: PrimitiveCaster(float), + SchemaType.BOOLEAN: PrimitiveCaster(forcebool), + SchemaType.OBJECT: DUMMY_CASTER, + SchemaType.ANY: DUMMY_CASTER, + } + COMPLEX_CASTERS = { + SchemaType.ARRAY: ArrayCaster, + } + + def create(self, schema): + if schema.type in self.PRIMITIVE_CASTERS: + return self.PRIMITIVE_CASTERS[schema.type] + elif schema.type in self.COMPLEX_CASTERS: + caster_class = self.COMPLEX_CASTERS[schema.type] + return caster_class(schema=schema, casters_factory=self) diff --git a/openapi_core/casting/schemas/util.py b/openapi_core/casting/schemas/util.py new file mode 100644 index 0000000..bb000a7 --- /dev/null +++ b/openapi_core/casting/schemas/util.py @@ -0,0 +1,10 @@ +"""OpenAPI core casting schemas util module""" +from distutils.util import strtobool +from six import string_types + + +def forcebool(val): + if isinstance(val, string_types): + val = strtobool(val) + + return bool(val) diff --git a/openapi_core/schema/media_types/models.py b/openapi_core/schema/media_types/models.py index ba31ef1..43a062e 100644 --- a/openapi_core/schema/media_types/models.py +++ b/openapi_core/schema/media_types/models.py @@ -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, + ValidateError, ) +from openapi_core.casting.schemas.exceptions import CastError from openapi_core.unmarshalling.schemas.exceptions import UnmarshalError diff --git a/openapi_core/schema/parameters/models.py b/openapi_core/schema/parameters/models.py index 61e1737..c06e198 100644 --- a/openapi_core/schema/parameters/models.py +++ b/openapi_core/schema/parameters/models.py @@ -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, + ValidateError, ) +from openapi_core.casting.schemas.exceptions import CastError from openapi_core.unmarshalling.schemas.exceptions import UnmarshalError log = logging.getLogger(__name__) diff --git a/openapi_core/schema/schemas/exceptions.py b/openapi_core/schema/schemas/exceptions.py index 308fdeb..5b0d808 100644 --- a/openapi_core/schema/schemas/exceptions.py +++ b/openapi_core/schema/schemas/exceptions.py @@ -7,17 +7,6 @@ class OpenAPISchemaError(OpenAPIMappingError): pass -@attr.s(hash=True) -class CastError(OpenAPISchemaError): - """Schema cast operation error""" - value = attr.ib() - type = attr.ib() - - def __str__(self): - return "Failed to cast value {value} to type {type}".format( - value=self.value, type=self.type) - - class ValidateError(OpenAPISchemaError): """Schema validate operation error""" pass diff --git a/openapi_core/schema/schemas/models.py b/openapi_core/schema/schemas/models.py index f18c4ee..b4190e1 100644 --- a/openapi_core/schema/schemas/models.py +++ b/openapi_core/schema/schemas/models.py @@ -1,18 +1,14 @@ """OpenAPI core schemas models module""" import attr import logging -from collections import defaultdict import re from jsonschema.exceptions import ValidationError 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, -) +from openapi_core.schema.schemas.exceptions import InvalidSchemaValue 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, @@ -30,12 +26,6 @@ class Format(object): class Schema(object): """Represents an OpenAPI Schema.""" - TYPE_CAST_CALLABLE_GETTER = { - SchemaType.INTEGER: int, - SchemaType.NUMBER: float, - SchemaType.BOOLEAN: forcebool, - } - def __init__( self, schema_type=None, properties=None, items=None, schema_format=None, required=None, default=NoValue, nullable=False, @@ -109,30 +99,17 @@ class Schema(object): all_properties = self.get_all_properties() return set(all_properties.keys()) - def get_cast_mapping(self): - mapping = self.TYPE_CAST_CALLABLE_GETTER.copy() - mapping.update({ - SchemaType.ARRAY: self._cast_collection, - }) - - return defaultdict(lambda: lambda x: x, mapping) - def cast(self, value): """Cast value from string to schema type""" - if value in (None, NoValue): - return value - - cast_mapping = self.get_cast_mapping() - - cast_callable = cast_mapping[self.type] + from openapi_core.casting.schemas.exceptions import CastError + from openapi_core.casting.schemas.factories import SchemaCastersFactory + casters_factory = SchemaCastersFactory() + caster = casters_factory.create(self) try: - return cast_callable(value) - except ValueError: + return caster(value) + except (ValueError, TypeError): raise CastError(value, self.type) - def _cast_collection(self, value): - return list(map(self.items.cast, value)) - def get_validator(self, resolver=None): return OAS30Validator( self.__dict__,