Skip to content

Commit 5273a7f

Browse files
committed
Separate cast and unmarshal
1 parent 0e30b71 commit 5273a7f

File tree

2 files changed

+62
-31
lines changed

2 files changed

+62
-31
lines changed

openapi_core/schema/parameters/models.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,16 @@ def unmarshal(self, value, custom_formatters=None):
108108
except (ValueError, AttributeError) as exc:
109109
raise InvalidParameterValue(self.name, exc)
110110

111+
try:
112+
casted = self.schema.cast(deserialized)
113+
except OpenAPISchemaError as exc:
114+
raise InvalidParameterValue(self.name, exc)
115+
111116
try:
112117
unmarshalled = self.schema.unmarshal(
113-
deserialized,
118+
casted,
114119
custom_formatters=custom_formatters,
115-
strict=False,
120+
strict=True,
116121
)
117122
except OpenAPISchemaError as exc:
118123
raise InvalidParameterValue(self.name, exc)

openapi_core/schema/schemas/models.py

+55-29
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ class Format(object):
3838
class Schema(object):
3939
"""Represents an OpenAPI Schema."""
4040

41-
DEFAULT_CAST_CALLABLE_GETTER = {
41+
TYPE_CAST_CALLABLE_GETTER = {
42+
SchemaType.INTEGER: int,
43+
SchemaType.NUMBER: float,
44+
SchemaType.BOOLEAN: forcebool,
45+
}
46+
47+
DEFAULT_UNMARSHAL_CALLABLE_GETTER = {
4248
}
4349

4450
STRING_FORMAT_CALLABLE_GETTER = {
@@ -155,7 +161,39 @@ def get_all_required_properties_names(self):
155161

156162
return set(required)
157163

158-
def get_cast_mapping(self, custom_formatters=None, strict=True):
164+
def are_additional_properties_allowed(self, one_of_schema=None):
165+
return (
166+
(self.additional_properties is not False) and
167+
(one_of_schema is None or
168+
one_of_schema.additional_properties is not False)
169+
)
170+
171+
def get_cast_mapping(self):
172+
mapping = self.TYPE_CAST_CALLABLE_GETTER.copy()
173+
mapping.update({
174+
SchemaType.ARRAY: self._cast_collection,
175+
})
176+
177+
return defaultdict(lambda: lambda x: x, mapping)
178+
179+
def cast(self, value):
180+
"""Cast value from string to schema type"""
181+
if value is None:
182+
return value
183+
184+
cast_mapping = self.get_cast_mapping()
185+
186+
cast_callable = cast_mapping[self.type]
187+
try:
188+
return cast_callable(value)
189+
except ValueError:
190+
raise InvalidSchemaValue(
191+
"Failed to cast value {value} to type {type}", value, self.type)
192+
193+
def _cast_collection(self, value):
194+
return list(map(self.items.cast, value))
195+
196+
def get_unmarshal_mapping(self, custom_formatters=None, strict=True):
159197
primitive_unmarshallers = self.get_primitive_unmarshallers(
160198
custom_formatters=custom_formatters)
161199

@@ -166,7 +204,7 @@ def get_cast_mapping(self, custom_formatters=None, strict=True):
166204

167205
pass_defaults = lambda f: functools.partial(
168206
f, custom_formatters=custom_formatters, strict=strict)
169-
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
207+
mapping = self.DEFAULT_UNMARSHAL_CALLABLE_GETTER.copy()
170208
mapping.update(primitive_unmarshallers_partial)
171209
mapping.update({
172210
SchemaType.ANY: pass_defaults(self._unmarshal_any),
@@ -176,15 +214,10 @@ def get_cast_mapping(self, custom_formatters=None, strict=True):
176214

177215
return defaultdict(lambda: lambda x: x, mapping)
178216

179-
def are_additional_properties_allowed(self, one_of_schema=None):
180-
return (
181-
(self.additional_properties is not False) and
182-
(one_of_schema is None or
183-
one_of_schema.additional_properties is not False)
184-
)
185-
186-
def cast(self, value, custom_formatters=None, strict=True):
187-
"""Cast value to schema type"""
217+
def unmarshal(self, value, custom_formatters=None, strict=True):
218+
"""Unmarshal parameter from the value."""
219+
if self.deprecated:
220+
warnings.warn("The schema is deprecated", DeprecationWarning)
188221
if value is None:
189222
if not self.nullable:
190223
raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type)
@@ -194,33 +227,26 @@ def cast(self, value, custom_formatters=None, strict=True):
194227
raise InvalidSchemaValue(
195228
"Value {value} not in enum choices: {type}", value, self.enum)
196229

197-
cast_mapping = self.get_cast_mapping(
230+
unmarshal_mapping = self.get_unmarshal_mapping(
198231
custom_formatters=custom_formatters, strict=strict)
199232

200233
if self.type is not SchemaType.STRING and value == '':
201234
return None
202235

203-
cast_callable = cast_mapping[self.type]
236+
unmarshal_callable = unmarshal_mapping[self.type]
204237
try:
205-
return cast_callable(value)
238+
unmarshalled = unmarshal_callable(value)
206239
except UnmarshallerStrictTypeError:
207240
raise InvalidSchemaValue(
208241
"Value {value} is not of type {type}", value, self.type)
209242
except ValueError:
210243
raise InvalidSchemaValue(
211244
"Failed to cast value {value} to type {type}", value, self.type)
212245

213-
def unmarshal(self, value, custom_formatters=None, strict=True):
214-
"""Unmarshal parameter from the value."""
215-
if self.deprecated:
216-
warnings.warn("The schema is deprecated", DeprecationWarning)
217-
218-
casted = self.cast(value, custom_formatters=custom_formatters, strict=strict)
219-
220-
if casted is None and not self.required:
246+
if unmarshalled is None and not self.required:
221247
return None
222248

223-
return casted
249+
return unmarshalled
224250

225251
def get_primitive_unmarshallers(self, **options):
226252
from openapi_core.schema.schemas.unmarshallers import (
@@ -247,28 +273,28 @@ def _unmarshal_any(self, value, custom_formatters=None, strict=True):
247273
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
248274
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
249275
]
250-
cast_mapping = self.get_cast_mapping()
276+
unmarshal_mapping = self.get_unmarshal_mapping()
251277
if self.one_of:
252278
result = None
253279
for subschema in self.one_of:
254280
try:
255-
casted = subschema.cast(value, custom_formatters)
281+
unmarshalled = subschema.unmarshal(value, custom_formatters)
256282
except (OpenAPISchemaError, TypeError, ValueError):
257283
continue
258284
else:
259285
if result is not None:
260286
raise MultipleOneOfSchema(self.type)
261-
result = casted
287+
result = unmarshalled
262288

263289
if result is None:
264290
raise NoOneOfSchema(self.type)
265291

266292
return result
267293
else:
268294
for schema_type in types_resolve_order:
269-
cast_callable = cast_mapping[schema_type]
295+
unmarshal_callable = unmarshal_mapping[schema_type]
270296
try:
271-
return cast_callable(value)
297+
return unmarshal_callable(value)
272298
except UnmarshallerStrictTypeError:
273299
continue
274300
# @todo: remove ValueError when validation separated

0 commit comments

Comments
 (0)