Separate cast and unmarshal

This commit is contained in:
p1c2u 2019-09-02 23:14:37 +01:00
parent 0e30b71a77
commit 5273a7ff7c
2 changed files with 62 additions and 31 deletions

View file

@ -108,11 +108,16 @@ class Parameter(object):
except (ValueError, AttributeError) as exc: except (ValueError, AttributeError) as exc:
raise InvalidParameterValue(self.name, exc) raise InvalidParameterValue(self.name, exc)
try:
casted = self.schema.cast(deserialized)
except OpenAPISchemaError as exc:
raise InvalidParameterValue(self.name, exc)
try: try:
unmarshalled = self.schema.unmarshal( unmarshalled = self.schema.unmarshal(
deserialized, casted,
custom_formatters=custom_formatters, custom_formatters=custom_formatters,
strict=False, strict=True,
) )
except OpenAPISchemaError as exc: except OpenAPISchemaError as exc:
raise InvalidParameterValue(self.name, exc) raise InvalidParameterValue(self.name, exc)

View file

@ -38,7 +38,13 @@ class Format(object):
class Schema(object): class Schema(object):
"""Represents an OpenAPI Schema.""" """Represents an OpenAPI Schema."""
DEFAULT_CAST_CALLABLE_GETTER = { TYPE_CAST_CALLABLE_GETTER = {
SchemaType.INTEGER: int,
SchemaType.NUMBER: float,
SchemaType.BOOLEAN: forcebool,
}
DEFAULT_UNMARSHAL_CALLABLE_GETTER = {
} }
STRING_FORMAT_CALLABLE_GETTER = { STRING_FORMAT_CALLABLE_GETTER = {
@ -155,7 +161,39 @@ class Schema(object):
return set(required) return set(required)
def get_cast_mapping(self, custom_formatters=None, strict=True): def are_additional_properties_allowed(self, one_of_schema=None):
return (
(self.additional_properties is not False) and
(one_of_schema is None or
one_of_schema.additional_properties is not False)
)
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 is None:
return value
cast_mapping = self.get_cast_mapping()
cast_callable = cast_mapping[self.type]
try:
return cast_callable(value)
except ValueError:
raise InvalidSchemaValue(
"Failed to cast value {value} to type {type}", value, self.type)
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( primitive_unmarshallers = self.get_primitive_unmarshallers(
custom_formatters=custom_formatters) custom_formatters=custom_formatters)
@ -166,7 +204,7 @@ class Schema(object):
pass_defaults = lambda f: functools.partial( pass_defaults = lambda f: functools.partial(
f, custom_formatters=custom_formatters, strict=strict) f, custom_formatters=custom_formatters, strict=strict)
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy() mapping = self.DEFAULT_UNMARSHAL_CALLABLE_GETTER.copy()
mapping.update(primitive_unmarshallers_partial) mapping.update(primitive_unmarshallers_partial)
mapping.update({ mapping.update({
SchemaType.ANY: pass_defaults(self._unmarshal_any), SchemaType.ANY: pass_defaults(self._unmarshal_any),
@ -176,15 +214,10 @@ class Schema(object):
return defaultdict(lambda: lambda x: x, mapping) return defaultdict(lambda: lambda x: x, mapping)
def are_additional_properties_allowed(self, one_of_schema=None): def unmarshal(self, value, custom_formatters=None, strict=True):
return ( """Unmarshal parameter from the value."""
(self.additional_properties is not False) and if self.deprecated:
(one_of_schema is None or warnings.warn("The schema is deprecated", DeprecationWarning)
one_of_schema.additional_properties is not False)
)
def cast(self, value, custom_formatters=None, strict=True):
"""Cast value to schema type"""
if value is None: if value is None:
if not self.nullable: if not self.nullable:
raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type) raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type)
@ -194,15 +227,15 @@ class Schema(object):
raise InvalidSchemaValue( raise InvalidSchemaValue(
"Value {value} not in enum choices: {type}", value, self.enum) "Value {value} not in enum choices: {type}", value, self.enum)
cast_mapping = self.get_cast_mapping( unmarshal_mapping = self.get_unmarshal_mapping(
custom_formatters=custom_formatters, strict=strict) custom_formatters=custom_formatters, strict=strict)
if self.type is not SchemaType.STRING and value == '': if self.type is not SchemaType.STRING and value == '':
return None return None
cast_callable = cast_mapping[self.type] unmarshal_callable = unmarshal_mapping[self.type]
try: try:
return cast_callable(value) unmarshalled = unmarshal_callable(value)
except UnmarshallerStrictTypeError: except UnmarshallerStrictTypeError:
raise InvalidSchemaValue( raise InvalidSchemaValue(
"Value {value} is not of type {type}", value, self.type) "Value {value} is not of type {type}", value, self.type)
@ -210,17 +243,10 @@ class Schema(object):
raise InvalidSchemaValue(