1
1
"""OpenAPI core schemas models module"""
2
2
import attr
3
- import functools
4
3
import logging
5
4
from collections import defaultdict
6
5
import re
7
- import warnings
8
6
9
7
from six import iteritems
10
8
from jsonschema .exceptions import ValidationError
11
9
12
- from openapi_core .extensions .models .factories import ModelFactory
13
10
from openapi_core .schema .schemas ._format import oas30_format_checker
14
11
from openapi_core .schema .schemas .enums import SchemaType
15
12
from openapi_core .schema .schemas .exceptions import (
16
- CastError , InvalidSchemaValue ,
17
- UnmarshalValueError , UnmarshalError ,
13
+ CastError , InvalidSchemaValue , UnmarshalValueError ,
18
14
)
15
+ from openapi_core .schema .schemas .types import NoValue
19
16
from openapi_core .schema .schemas .util import forcebool
20
17
from openapi_core .schema .schemas .validators import OAS30Validator
21
18
@@ -41,16 +38,16 @@ class Schema(object):
41
38
}
42
39
43
40
def __init__ (
44
- self , schema_type = None , model = None , properties = None , items = None ,
45
- schema_format = None , required = None , default = None , nullable = False ,
41
+ self , schema_type = None , properties = None , items = None ,
42
+ schema_format = None , required = None , default = NoValue , nullable = False ,
46
43
enum = None , deprecated = False , all_of = None , one_of = None ,
47
44
additional_properties = True , min_items = None , max_items = None ,
48
45
min_length = None , max_length = None , pattern = None , unique_items = False ,
49
46
minimum = None , maximum = None , multiple_of = None ,
50
47
exclusive_minimum = False , exclusive_maximum = False ,
51
- min_properties = None , max_properties = None , _source = None ):
48
+ min_properties = None , max_properties = None , extensions = None ,
49
+ _source = None ):
52
50
self .type = SchemaType (schema_type )
53
- self .model = model
54
51
self .properties = properties and dict (properties ) or {}
55
52
self .items = items
56
53
self .format = schema_format
@@ -79,6 +76,8 @@ def __init__(
79
76
self .max_properties = int (max_properties )\
80
77
if max_properties is not None else None
81
78
79
+ self .extensions = extensions and dict (extensions ) or {}
80
+
82
81
self ._all_required_properties_cache = None
83
82
self ._all_optional_properties_cache = None
84
83
@@ -95,6 +94,9 @@ def to_dict(self):
95
94
def __getitem__ (self , name ):
96
95
return self .properties [name ]
97
96
97
+ def has_default (self ):
98
+ return self .default is not NoValue
99
+
98
100
def get_all_properties (self ):
99
101
properties = self .properties .copy ()
100
102
@@ -144,7 +146,7 @@ def get_cast_mapping(self):
144
146
145
147
def cast (self , value ):
146
148
"""Cast value from string to schema type"""
147
- if value is None :
149
+ if value in ( None , NoValue ) :
148
150
return value
149
151
150
152
cast_mapping = self .get_cast_mapping ()
@@ -158,28 +160,6 @@ def cast(self, value):
158
160
def _cast_collection (self , value ):
159
161
return list (map (self .items .cast , value ))
160
162
161
- def get_unmarshal_mapping (self , custom_formatters = None , strict = True ):
162
- primitive_unmarshallers = self .get_primitive_unmarshallers (
163
- custom_formatters = custom_formatters )
164
-
165
- primitive_unmarshallers_partial = dict (
166
- (t , functools .partial (u , type_format = self .format , strict = strict ))
167
- for t , u in primitive_unmarshallers .items ()
168
- )
169
-
170
- def pass_defaults (f ):
171
- return functools .partial (
172
- f , custom_formatters = custom_formatters , strict = strict )
173
- mapping = self .DEFAULT_UNMARSHAL_CALLABLE_GETTER .copy ()
174
- mapping .update (primitive_unmarshallers_partial )
175
- mapping .update ({
176
- SchemaType .ANY : pass_defaults (self ._unmarshal_any ),
177
- SchemaType .ARRAY : pass_defaults (self ._unmarshal_collection ),
178
- SchemaType .OBJECT : pass_defaults (self ._unmarshal_object ),
179
- })
180
-
181
- return defaultdict (lambda : lambda x : x , mapping )
182
-
183
163
def get_validator (self , resolver = None ):
184
164
return OAS30Validator (
185
165
self .__dict__ ,
@@ -197,162 +177,13 @@ def validate(self, value, resolver=None):
197
177
198
178
def unmarshal (self , value , custom_formatters = None , strict = True ):
199
179
"""Unmarshal parameter from the value."""
200
- if self .deprecated :
201
- warnings .warn ("The schema is deprecated" , DeprecationWarning )
202
- if value is None :
203
- if not self .nullable :
204
- raise UnmarshalError (
205
- "Null value for non-nullable schema" , value , self .type )
206
- return self .default
207
-
208
- if self .enum and value not in self .enum :
209
- raise UnmarshalError ("Invalid value for enum: {0}" .format (value ))
210
-
211
- unmarshal_mapping = self .get_unmarshal_mapping (
212
- custom_formatters = custom_formatters , strict = strict )
213
-
214
- if self .type is not SchemaType .STRING and value == '' :
215
- return None
216
-
217
- unmarshal_callable = unmarshal_mapping [self .type ]
180
+ from openapi_core .unmarshalling .schemas .factories import (
181
+ SchemaUnmarshallersFactory ,
182
+ )
183
+ unmarshallers_factory = SchemaUnmarshallersFactory (
184
+ custom_formatters )
185
+ unmarshaller = unmarshallers_factory .create (self )
218
186
try :
219
- unmarshalled = unmarshal_callable (value )
187
+ return unmarshaller (value , strict = strict )
220
188
except ValueError as exc :
221
189
raise UnmarshalValueError (value , self .type , exc )
222
-
223
- return unmarshalled
224
-
225
- def get_primitive_unmarshallers (self , ** options ):
226
- from openapi_core .schema .schemas .unmarshallers import (
227
- StringUnmarshaller , BooleanUnmarshaller , IntegerUnmarshaller ,
228
- NumberUnmarshaller ,
229
- )
230
-
231
- unmarshallers_classes = {
232
- SchemaType .STRING : StringUnmarshaller ,
233
- SchemaType .BOOLEAN : BooleanUnmarshaller ,
234
- SchemaType .INTEGER : IntegerUnmarshaller ,
235
- SchemaType .NUMBER : NumberUnmarshaller ,
236
- }
237
-
238
- unmarshallers = dict (
239
- (t , klass (** options ))
240
- for t , klass in unmarshallers_classes .items ()
241
- )
242
-
243
- return unmarshallers
244
-
245
- def _unmarshal_any (self , value , custom_formatters = None , strict = True ):
246
- types_resolve_order = [
247
- SchemaType .OBJECT , SchemaType .ARRAY , SchemaType .BOOLEAN ,
248
- SchemaType .INTEGER , SchemaType .NUMBER , SchemaType .STRING ,
249
- ]
250
- unmarshal_mapping = self .get_unmarshal_mapping ()
251
- if self .one_of :
252
- result = None
253
- for subschema in self .one_of :
254
- try :
255
- unmarshalled = subschema .unmarshal (
256
- value , custom_formatters )
257
- except UnmarshalError :
258
- continue
259
- else :
260
- if result is not None :
261
- log .warning ("multiple valid oneOf schemas found" )
262
- continue
263
- result = unmarshalled
264
-
265
- if result is None :
266
- log .warning ("valid oneOf schema not found" )
267
-
268
- return result
269
- else :
270
- for schema_type in types_resolve_order :
271
- unmarshal_callable = unmarshal_mapping [schema_type ]
272
- try :
273
- return unmarshal_callable (value )
274
- except (UnmarshalError , ValueError ):
275
- continue
276
-
277
- log .warning ("failed to unmarshal any type" )
278
- return value
279
-
280
- def _unmarshal_collection (
281
- self , value , custom_formatters = None , strict = True ):
282
- if not isinstance (value , (list , tuple )):
283
- raise ValueError (
284
- "Invalid value for collection: {0}" .format (value ))
285
-
286
- f = functools .partial (
287
- self .items .unmarshal ,
288
- custom_formatters = custom_formatters , strict = strict ,
289
- )
290
- return list (map (f , value ))
291
-
292
- def _unmarshal_object (self , value , model_factory = None ,
293
- custom_formatters = None , strict = True ):
294
- if not isinstance (value , (dict , )):
295
- raise ValueError ("Invalid value for object: {0}" .format (value ))
296
-
297
- model_factory = model_factory or ModelFactory ()
298
-
299
- if self .one_of :
300
- properties = None
301
- for one_of_schema in self .one_of :
302
- try :
303
- unmarshalled = self ._unmarshal_properties (
304
- value , one_of_schema ,
305
- custom_formatters = custom_formatters ,
306
- )
307
- except (UnmarshalError , ValueError ):
308
- pass
309
- else :
310
- if properties is not None :
311
- log .warning ("multiple valid oneOf schemas found" )
312
- continue
313
- properties = unmarshalled
314
-
315
- if properties is None :
316
- log .warning ("valid oneOf schema not found" )
317
-
318
- else :
319
- properties = self ._unmarshal_properties (
320
- value , custom_formatters = custom_formatters )
321
-
322
- return model_factory .create (properties , name = self .model )
323
-
324
- def _unmarshal_properties (self , value , one_of_schema = None ,
325
- custom_formatters = None , strict = True ):
326
- all_props = self .get_all_properties ()
327
- all_props_names = self .get_all_properties_names ()
328
- all_req_props_names = self .get_all_required_properties_names ()
329
-
330
- if one_of_schema is not None :
331
- all_props .update (one_of_schema .get_all_properties ())
332
- all_props_names |= one_of_schema .\
333
- get_all_properties_names ()
334
- all_req_props_names |= one_of_schema .\
335
- get_all_required_properties_names ()
336
-
337
- value_props_names = value .keys ()
338
- extra_props = set (value_props_names ) - set (all_props_names )
339
-
340
- properties = {}
341
- if self .additional_properties is not True :
342
- for prop_name in extra_props :
343
- prop_value = value [prop_name ]
344
- properties [prop_name ] = self .additional_properties .unmarshal (
345
- prop_value , custom_formatters = custom_formatters )
346
-
347
- for prop_name , prop in iteritems (all_props ):
348
- try :
349
- prop_value = value [prop_name ]
350
- except KeyError :
351
- if not prop .nullable and not prop .default :
352
- continue
353
- prop_value = prop .default
354
-
355
- properties [prop_name ] = prop .unmarshal (
356
- prop_value , custom_formatters = custom_formatters )
357
-
358
- return properties
0 commit comments