Skip to content

Commit beaa08a

Browse files
authored
Merge branch 'master' into master
2 parents a08b620 + aa978cb commit beaa08a

File tree

7 files changed

+356
-56
lines changed

7 files changed

+356
-56
lines changed

openapi_core/schema/parameters/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ def unmarshal(self, value, custom_formatters=None):
109109
raise InvalidParameterValue(self.name, exc)
110110

111111
try:
112-
unmarshalled = self.schema.unmarshal(deserialized, custom_formatters=custom_formatters)
112+
unmarshalled = self.schema.unmarshal(
113+
deserialized,
114+
custom_formatters=custom_formatters,
115+
strict=False,
116+
)
113117
except OpenAPISchemaError as exc:
114118
raise InvalidParameterValue(self.name, exc)
115119

openapi_core/schema/schemas/factories.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def create(self, schema_spec):
2828
deprecated = schema_deref.get('deprecated', False)
2929
all_of_spec = schema_deref.get('allOf', None)
3030
one_of_spec = schema_deref.get('oneOf', None)
31-
additional_properties_spec = schema_deref.get('additionalProperties')
31+
additional_properties_spec = schema_deref.get('additionalProperties',
32+
True)
3233
min_items = schema_deref.get('minItems', None)
3334
max_items = schema_deref.get('maxItems', None)
3435
min_length = schema_deref.get('minLength', None)
@@ -59,8 +60,8 @@ def create(self, schema_spec):
5960
if items_spec:
6061
items = self._create_items(items_spec)
6162

62-
additional_properties = None
63-
if additional_properties_spec:
63+
additional_properties = additional_properties_spec
64+
if isinstance(additional_properties_spec, dict):
6465
additional_properties = self.create(additional_properties_spec)
6566

6667
return Schema(

openapi_core/schema/schemas/models.py

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ class Schema(object):
3939
"""Represents an OpenAPI Schema."""
4040

4141
DEFAULT_CAST_CALLABLE_GETTER = {
42-
SchemaType.INTEGER: int,
43-
SchemaType.NUMBER: float,
44-
SchemaType.BOOLEAN: forcebool,
4542
}
4643

4744
STRING_FORMAT_CALLABLE_GETTER = {
@@ -69,7 +66,7 @@ def __init__(
6966
self, schema_type=None, model=None, properties=None, items=None,
7067
schema_format=None, required=None, default=None, nullable=False,
7168
enum=None, deprecated=False, all_of=None, one_of=None,
72-
additional_properties=None, min_items=None, max_items=None,
69+
additional_properties=True, min_items=None, max_items=None,
7370
min_length=None, max_length=None, pattern=None, unique_items=False,
7471
minimum=None, maximum=None, multiple_of=None,
7572
exclusive_minimum=False, exclusive_maximum=False,
@@ -149,27 +146,31 @@ def get_all_required_properties_names(self):
149146

150147
return set(required)
151148

152-
def get_cast_mapping(self, custom_formatters=None):
149+
def get_cast_mapping(self, custom_formatters=None, strict=True):
153150
pass_defaults = lambda f: functools.partial(
154-
f, custom_formatters=custom_formatters)
151+
f, custom_formatters=custom_formatters, strict=strict)
155152
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
156153
mapping.update({
157154
SchemaType.STRING: pass_defaults(self._unmarshal_string),
155+
SchemaType.BOOLEAN: pass_defaults(self._unmarshal_boolean),
156+
SchemaType.INTEGER: pass_defaults(self._unmarshal_integer),
157+
SchemaType.NUMBER: pass_defaults(self._unmarshal_number),
158158
SchemaType.ANY: pass_defaults(self._unmarshal_any),
159159
SchemaType.ARRAY: pass_defaults(self._unmarshal_collection),
160160
SchemaType.OBJECT: pass_defaults(self._unmarshal_object),
161161
})
162162

163163
return defaultdict(lambda: lambda x: x, mapping)
164164

165-
def cast(self, value, custom_formatters=None):
165+
def cast(self, value, custom_formatters=None, strict=True):
166166
"""Cast value to schema type"""
167167
if value is None:
168168
if not self.nullable:
169169
raise InvalidSchemaValue("Null value for non-nullable schema", value, self.type)
170170
return self.default
171171

172-
cast_mapping = self.get_cast_mapping(custom_formatters=custom_formatters)
172+
cast_mapping = self.get_cast_mapping(
173+
custom_formatters=custom_formatters, strict=strict)
173174

174175
if self.type is not SchemaType.STRING and value == '':
175176
return None
@@ -181,12 +182,12 @@ def cast(self, value, custom_formatters=None):
181182
raise InvalidSchemaValue(
182183
"Failed to cast value {value} to type {type}", value, self.type)
183184

184-
def unmarshal(self, value, custom_formatters=None):
185+
def unmarshal(self, value, custom_formatters=None, strict=True):
185186
"""Unmarshal parameter from the value."""
186187
if self.deprecated:
187188
warnings.warn("The schema is deprecated", DeprecationWarning)
188189

189-
casted = self.cast(value, custom_formatters=custom_formatters)
190+
casted = self.cast(value, custom_formatters=custom_formatters, strict=strict)
190191

191192
if casted is None and not self.required:
192193
return None
@@ -197,7 +198,10 @@ def unmarshal(self, value, custom_formatters=None):
197198

198199
return casted
199200

200-
def _unmarshal_string(self, value, custom_formatters=None):
201+
def _unmarshal_string(self, value, custom_formatters=None, strict=True):
202+
if strict and not isinstance(value, (text_type, binary_type)):
203+
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
204+
201205
try:
202206
schema_format = SchemaFormat(self.format)
203207
except ValueError:
@@ -217,7 +221,25 @@ def _unmarshal_string(self, value, custom_formatters=None):
217221
raise InvalidCustomFormatSchemaValue(
218222
"Failed to format value {value} to format {type}: {exception}", value, self.format, exc)
219223

220-
def _unmarshal_any(self, value, custom_formatters=None):
224+
def _unmarshal_integer(self, value, custom_formatters=None, strict=True):
225+
if strict and not isinstance(value, (integer_types, )):
226+
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
227+
228+
return int(value)
229+
230+
def _unmarshal_number(self, value, custom_formatters=None, strict=True):
231+
if strict and not isinstance(value, (float, )):
232+
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
233+
234+
return float(value)
235+
236+
def _unmarshal_boolean(self, value, custom_formatters=None, strict=True):
237+
if strict and not isinstance(value, (bool, )):
238+
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
239+
240+
return forcebool(value)
241+
242+
def _unmarshal_any(self, value, custom_formatters=None, strict=True):
221243
types_resolve_order = [
222244
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
223245
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
@@ -233,16 +255,21 @@ def _unmarshal_any(self, value, custom_formatters=None):
233255

234256
raise NoValidSchema(value)
235257

236-
def _unmarshal_collection(self, value, custom_formatters=None):
258+
def _unmarshal_collection(self, value, custom_formatters=None, strict=True):
259+
if not isinstance(value, (list, tuple)):
260+
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
261+
237262
if self.items is None:
238263
raise UndefinedItemsSchema(self.type)
239264

240-
f = functools.partial(self.items.unmarshal,
241-
custom_formatters=custom_formatters)
265+
f = functools.partial(
266+
self.items.unmarshal,
267+
custom_formatters=custom_formatters, strict=strict,
268+
)
242269
return list(map(f, value))
243270

244271
def _unmarshal_object(self, value, model_factory=None,
245-
custom_formatters=None):
272+
custom_formatters=None, strict=True):
246273
if not isinstance(value, (dict, )):
247274
raise InvalidSchemaValue("Value {value} is not of type {type}", value, self.type)
248275

@@ -271,7 +298,7 @@ def _unmarshal_object(self, value, model_factory=None,
271298
return model_factory.create(properties, name=self.model)
272299

273300
def _unmarshal_properties(self, value, one_of_schema=None,
274-
custom_formatters=None):
301+
custom_formatters=None, strict=True):
275302
all_props = self.get_all_properties()
276303
all_props_names = self.get_all_properties_names()
277304
all_req_props_names = self.get_all_required_properties_names()
@@ -285,14 +312,15 @@ def _unmarshal_properties(self, value, one_of_schema=None,
285312

286313
value_props_names = value.keys()
287314
extra_props = set(value_props_names) - set(all_props_names)
288-
if extra_props and self.additional_properties is None:
315+
if extra_props and self.additional_properties is False:
289316
raise UndefinedSchemaProperty(extra_props)
290317

291318
properties = {}
292-
for prop_name in extra_props:
293-
prop_value = value[prop_name]
294-
properties[prop_name] = self.additional_properties.unmarshal(
295-
prop_value, custom_formatters=custom_formatters)
319+
if self.additional_properties is not True:
320+
for prop_name in extra_props:
321+
prop_value = value[prop_name]
322+
properties[prop_name] = self.additional_properties.unmarshal(
323+
prop_value, custom_formatters=custom_formatters)
296324

297325
for prop_name, prop in iteritems(all_props):
298326
try:
@@ -516,13 +544,14 @@ def _validate_properties(self, value, one_of_schema=None,
516544

517545
value_props_names = value.keys()
518546
extra_props = set(value_props_names) - set(all_props_names)
519-
if extra_props and self.additional_properties is None:
547+
if extra_props and self.additional_properties is False:
520548
raise UndefinedSchemaProperty(extra_props)
521549

522-
for prop_name in extra_props:
523-
prop_value = value[prop_name]
524-
self.additional_properties.validate(
525-
prop_value, custom_formatters=custom_formatters)
550+
if self.additional_properties is not True:
551+
for prop_name in extra_props:
552+
prop_value = value[prop_name]
553+
self.additional_properties.validate(
554+
prop_value, custom_formatters=custom_formatters)
526555

527556
for prop_name, prop in iteritems(all_props):
528557
try:

tests/integration/data/v3.0/petstore.yaml

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,7 @@ paths:
5959
explode: false
6060
responses:
6161
'200':
62-
description: An paged array of pets
63-
headers:
64-
x-next:
65-
description: A link to the next page of responses
66-
schema:
67-
type: string
68-
content:
69-
application/json:
70-
schema:
71-
$ref: "#/components/schemas/PetsData"
62+
$ref: "#/components/responses/PetsResponse"
7263
post:
7364
summary: Create a pet
7465
operationId: createPets
@@ -295,6 +286,7 @@ components:
295286
$ref: "#/components/schemas/Utctime"
296287
name:
297288
type: string
289+
additionalProperties: false
298290
TagList:
299291
type: array
300292
items:
@@ -327,9 +319,20 @@ components:
327319
additionalProperties:
328320
type: string
329321
responses:
330-
ErrorResponse:
331-
description: unexpected error
332-
content:
333-
application/json:
334-
schema:
335-
$ref: "#/components/schemas/ExtendedError"
322+
ErrorResponse:
323+
description: unexpected error
324+
content:
325+
application/json:
326+
schema:
327+
$ref: "#/components/schemas/ExtendedError"
328+
PetsResponse:
329+
description: An paged array of pets
330+
headers:
331+
x-next:
332+
description: A link to the next page of responses
333+
schema:
334+
type: string
335+
content:
336+
application/json:
337+
schema:
338+
$ref: "#/components/schemas/PetsData"

0 commit comments

Comments
 (0)