pinecone.core.client.model_utils
Pinecone API
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501
The version of the OpenAPI document: version not set Contact: support@pinecone.io Generated by: https://openapi-generator.tech
1""" 2 Pinecone API 3 4 No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501 5 6 The version of the OpenAPI document: version not set 7 Contact: support@pinecone.io 8 Generated by: https://openapi-generator.tech 9""" 10 11 12from datetime import date, datetime # noqa: F401 13import inspect 14import io 15import os 16import pprint 17import re 18import tempfile 19 20from dateutil.parser import parse 21 22from pinecone.core.client.exceptions import ( 23 ApiKeyError, 24 ApiAttributeError, 25 ApiTypeError, 26 ApiValueError, 27) 28 29none_type = type(None) 30file_type = io.IOBase 31 32 33def convert_js_args_to_python_args(fn): 34 from functools import wraps 35 36 @wraps(fn) 37 def wrapped_init(_self, *args, **kwargs): 38 """ 39 An attribute named `self` received from the api will conflicts with the reserved `self` 40 parameter of a class method. During generation, `self` attributes are mapped 41 to `_self` in models. Here, we name `_self` instead of `self` to avoid conflicts. 42 """ 43 spec_property_naming = kwargs.get("_spec_property_naming", False) 44 if spec_property_naming: 45 kwargs = change_keys_js_to_python(kwargs, _self if isinstance(_self, type) else _self.__class__) 46 return fn(_self, *args, **kwargs) 47 48 return wrapped_init 49 50 51class cached_property(object): 52 # this caches the result of the function call for fn with no inputs 53 # use this as a decorator on function methods that you want converted 54 # into cached properties 55 result_key = "_results" 56 57 def __init__(self, fn): 58 self._fn = fn 59 60 def __get__(self, instance, cls=None): 61 if self.result_key in vars(self): 62 return vars(self)[self.result_key] 63 else: 64 result = self._fn() 65 setattr(self, self.result_key, result) 66 return result 67 68 69PRIMITIVE_TYPES = (list, float, int, bool, datetime, date, str, file_type) 70 71 72def allows_single_value_input(cls): 73 """ 74 This function returns True if the input composed schema model or any 75 descendant model allows a value only input 76 This is true for cases where oneOf contains items like: 77 oneOf: 78 - float 79 - NumberWithValidation 80 - StringEnum 81 - ArrayModel 82 - null 83 TODO: lru_cache this 84 """ 85 if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 86 return True 87 elif issubclass(cls, ModelComposed): 88 if not cls._composed_schemas["oneOf"]: 89 return False 90 return any(allows_single_value_input(c) for c in cls._composed_schemas["oneOf"]) 91 return False 92 93 94def composed_model_input_classes(cls): 95 """ 96 This function returns a list of the possible models that can be accepted as 97 inputs. 98 TODO: lru_cache this 99 """ 100 if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 101 return [cls] 102 elif issubclass(cls, ModelNormal): 103 if cls.discriminator is None: 104 return [cls] 105 else: 106 return get_discriminated_classes(cls) 107 elif issubclass(cls, ModelComposed): 108 if not cls._composed_schemas["oneOf"]: 109 return [] 110 if cls.discriminator is None: 111 input_classes = [] 112 for c in cls._composed_schemas["oneOf"]: 113 input_classes.extend(composed_model_input_classes(c)) 114 return input_classes 115 else: 116 return get_discriminated_classes(cls) 117 return [] 118 119 120class OpenApiModel(object): 121 """The base class for all OpenAPIModels""" 122 123 def set_attribute(self, name, value): 124 # this is only used to set properties on self 125 126 path_to_item = [] 127 if self._path_to_item: 128 path_to_item.extend(self._path_to_item) 129 path_to_item.append(name) 130 131 if name in self.openapi_types: 132 required_types_mixed = self.openapi_types[name] 133 elif self.additional_properties_type is None: 134 raise ApiAttributeError("{0} has no attribute '{1}'".format(type(self).__name__, name), path_to_item) 135 elif self.additional_properties_type is not None: 136 required_types_mixed = self.additional_properties_type 137 138 if get_simple_class(name) != str: 139 error_msg = type_error_message(var_name=name, var_value=name, valid_classes=(str,), key_type=True) 140 raise ApiTypeError(error_msg, path_to_item=path_to_item, valid_classes=(str,), key_type=True) 141 142 if self._check_type: 143 value = validate_and_convert_types( 144 value, 145 required_types_mixed, 146 path_to_item, 147 self._spec_property_naming, 148 self._check_type, 149 configuration=self._configuration, 150 ) 151 if (name,) in self.allowed_values: 152 check_allowed_values(self.allowed_values, (name,), value) 153 if (name,) in self.validations: 154 check_validations(self.validations, (name,), value, self._configuration) 155 self.__dict__["_data_store"][name] = value 156 157 def __repr__(self): 158 """For `print` and `pprint`""" 159 return self.to_str() 160 161 def __ne__(self, other): 162 """Returns true if both objects are not equal""" 163 return not self == other 164 165 def __setattr__(self, attr, value): 166 """set the value of an attribute using dot notation: `instance.attr = val`""" 167 self[attr] = value 168 169 def __getattr__(self, attr): 170 """get the value of an attribute using dot notation: `instance.attr`""" 171 return self.get(attr) 172 173 def __new__(cls, *args, **kwargs): 174 # this function uses the discriminator to 175 # pick a new schema/class to instantiate because a discriminator 176 # propertyName value was passed in 177 178 if len(args) == 1: 179 arg = args[0] 180 if arg is None and is_type_nullable(cls): 181 # The input data is the 'null' value and the type is nullable. 182 return None 183 184 if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 185 model_kwargs = {} 186 oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) 187 return oneof_instance 188 189 visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 190 if cls.discriminator is None or cls in visited_composed_classes: 191 # Use case 1: this openapi schema (cls) does not have a discriminator 192 # Use case 2: we have already visited this class before and are sure that we 193 # want to instantiate it this time. We have visited this class deserializing 194 # a payload with a discriminator. During that process we traveled through 195 # this class but did not make an instance of it. Now we are making an 196 # instance of a composed class which contains cls in it, so this time make an instance of cls. 197 # 198 # Here's an example of use case 2: If Animal has a discriminator 199 # petType and we pass in "Dog", and the class Dog 200 # allOf includes Animal, we move through Animal 201 # once using the discriminator, and pick Dog. 202 # Then in the composed schema dog Dog, we will make an instance of the 203 # Animal class (because Dal has allOf: Animal) but this time we won't travel 204 # through Animal's discriminator because we passed in 205 # _visited_composed_classes = (Animal,) 206 207 return super(OpenApiModel, cls).__new__(cls) 208 209 # Get the name and value of the discriminator property. 210 # The discriminator name is obtained from the discriminator meta-data 211 # and the discriminator value is obtained from the input data. 212 discr_propertyname_py = list(cls.discriminator.keys())[0] 213 discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 214 if discr_propertyname_js in kwargs: 215 discr_value = kwargs[discr_propertyname_js] 216 elif discr_propertyname_py in kwargs: 217 discr_value = kwargs[discr_propertyname_py] 218 else: 219 # The input data does not contain the discriminator property. 220 path_to_item = kwargs.get("_path_to_item", ()) 221 raise ApiValueError( 222 "Cannot deserialize input data due to missing discriminator. " 223 "The discriminator property '%s' is missing at path: %s" % (discr_propertyname_js, path_to_item) 224 ) 225 226 # Implementation note: the last argument to get_discriminator_class 227 # is a list of visited classes. get_discriminator_class may recursively 228 # call itself and update the list of visited classes, and the initial 229 # value must be an empty list. Hence not using 'visited_composed_classes' 230 new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 231 if new_cls is None: 232 path_to_item = kwargs.get("_path_to_item", ()) 233 disc_prop_value = kwargs.get(discr_propertyname_js, kwargs.get(discr_propertyname_py)) 234 raise ApiValueError( 235 "Cannot deserialize input data due to invalid discriminator " 236 "value. The OpenAPI document has no mapping for discriminator " 237 "property '%s'='%s' at path: %s" % (discr_propertyname_js, disc_prop_value, path_to_item) 238 ) 239 240 if new_cls in visited_composed_classes: 241 # if we are making an instance of a composed schema Descendent 242 # which allOf includes Ancestor, then Ancestor contains 243 # a discriminator that includes Descendent. 244 # So if we make an instance of Descendent, we have to make an 245 # instance of Ancestor to hold the allOf properties. 246 # This code detects that use case and makes the instance of Ancestor 247 # For example: 248 # When making an instance of Dog, _visited_composed_classes = (Dog,) 249 # then we make an instance of Animal to include in dog._composed_instances 250 # so when we are here, cls is Animal 251 # cls.discriminator != None 252 # cls not in _visited_composed_classes 253 # new_cls = Dog 254 # but we know we know that we already have Dog 255 # because it is in visited_composed_classes 256 # so make Animal here 257 return super(OpenApiModel, cls).__new__(cls) 258 259 # Build a list containing all oneOf and anyOf descendants. 260 oneof_anyof_classes = None 261 if cls._composed_schemas is not None: 262 oneof_anyof_classes = cls._composed_schemas.get("oneOf", ()) + cls._composed_schemas.get("anyOf", ()) 263 oneof_anyof_child = new_cls in oneof_anyof_classes 264 kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 265 266 if cls._composed_schemas.get("allOf") and oneof_anyof_child: 267 # Validate that we can make self because when we make the 268 # new_cls it will not include the allOf validations in self 269 self_inst = super(OpenApiModel, cls).__new__(cls) 270 self_inst.__init__(*args, **kwargs) 271 272 new_inst = new_cls.__new__(new_cls, *args, **kwargs) 273 new_inst.__init__(*args, **kwargs) 274 return new_inst 275 276 @classmethod 277 @convert_js_args_to_python_args 278 def _new_from_openapi_data(cls, *args, **kwargs): 279 # this function uses the discriminator to 280 # pick a new schema/class to instantiate because a discriminator 281 # propertyName value was passed in 282 283 if len(args) == 1: 284 arg = args[0] 285 if arg is None and is_type_nullable(cls): 286 # The input data is the 'null' value and the type is nullable. 287 return None 288 289 if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 290 model_kwargs = {} 291 oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) 292 return oneof_instance 293 294 visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 295 if cls.discriminator is None or cls in visited_composed_classes: 296 # Use case 1: this openapi schema (cls) does not have a discriminator 297 # Use case 2: we have already visited this class before and are sure that we 298 # want to instantiate it this time. We have visited this class deserializing 299 # a payload with a discriminator. During that process we traveled through 300 # this class but did not make an instance of it. Now we are making an 301 # instance of a composed class which contains cls in it, so this time make an instance of cls. 302 # 303 # Here's an example of use case 2: If Animal has a discriminator 304 # petType and we pass in "Dog", and the class Dog 305 # allOf includes Animal, we move through Animal 306 # once using the discriminator, and pick Dog. 307 # Then in the composed schema dog Dog, we will make an instance of the 308 # Animal class (because Dal has allOf: Animal) but this time we won't travel 309 # through Animal's discriminator because we passed in 310 # _visited_composed_classes = (Animal,) 311 312 return cls._from_openapi_data(*args, **kwargs) 313 314 # Get the name and value of the discriminator property. 315 # The discriminator name is obtained from the discriminator meta-data 316 # and the discriminator value is obtained from the input data. 317 discr_propertyname_py = list(cls.discriminator.keys())[0] 318 discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 319 if discr_propertyname_js in kwargs: 320 discr_value = kwargs[discr_propertyname_js] 321 elif discr_propertyname_py in kwargs: 322 discr_value = kwargs[discr_propertyname_py] 323 else: 324 # The input data does not contain the discriminator property. 325 path_to_item = kwargs.get("_path_to_item", ()) 326 raise ApiValueError( 327 "Cannot deserialize input data due to missing discriminator. " 328 "The discriminator property '%s' is missing at path: %s" % (discr_propertyname_js, path_to_item) 329 ) 330 331 # Implementation note: the last argument to get_discriminator_class 332 # is a list of visited classes. get_discriminator_class may recursively 333 # call itself and update the list of visited classes, and the initial 334 # value must be an empty list. Hence not using 'visited_composed_classes' 335 new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 336 if new_cls is None: 337 path_to_item = kwargs.get("_path_to_item", ()) 338 disc_prop_value = kwargs.get(discr_propertyname_js, kwargs.get(discr_propertyname_py)) 339 raise ApiValueError( 340 "Cannot deserialize input data due to invalid discriminator " 341 "value. The OpenAPI document has no mapping for discriminator " 342 "property '%s'='%s' at path: %s" % (discr_propertyname_js, disc_prop_value, path_to_item) 343 ) 344 345 if new_cls in visited_composed_classes: 346 # if we are making an instance of a composed schema Descendent 347 # which allOf includes Ancestor, then Ancestor contains 348 # a discriminator that includes Descendent. 349 # So if we make an instance of Descendent, we have to make an 350 # instance of Ancestor to hold the allOf properties. 351 # This code detects that use case and makes the instance of Ancestor 352 # For example: 353 # When making an instance of Dog, _visited_composed_classes = (Dog,) 354 # then we make an instance of Animal to include in dog._composed_instances 355 # so when we are here, cls is Animal 356 # cls.discriminator != None 357 # cls not in _visited_composed_classes 358 # new_cls = Dog 359 # but we know we know that we already have Dog 360 # because it is in visited_composed_classes 361 # so make Animal here 362 return cls._from_openapi_data(*args, **kwargs) 363 364 # Build a list containing all oneOf and anyOf descendants. 365 oneof_anyof_classes = None 366 if cls._composed_schemas is not None: 367 oneof_anyof_classes = cls._composed_schemas.get("oneOf", ()) + cls._composed_schemas.get("anyOf", ()) 368 oneof_anyof_child = new_cls in oneof_anyof_classes 369 kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 370 371 if cls._composed_schemas.get("allOf") and oneof_anyof_child: 372 # Validate that we can make self because when we make the 373 # new_cls it will not include the allOf validations in self 374 self_inst = cls._from_openapi_data(*args, **kwargs) 375 376 new_inst = new_cls._new_from_openapi_data(*args, **kwargs) 377 return new_inst 378 379 380class ModelSimple(OpenApiModel): 381 """the parent class of models whose type != object in their 382 swagger/openapi""" 383 384 def __setitem__(self, name, value): 385 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 386 if name in self.required_properties: 387 self.__dict__[name] = value 388 return 389 390 self.set_attribute(name, value) 391 392 def get(self, name, default=None): 393 """returns the value of an attribute or some default value if the attribute was not set""" 394 if name in self.required_properties: 395 return self.__dict__[name] 396 397 return self.__dict__["_data_store"].get(name, default) 398 399 def __getitem__(self, name): 400 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 401 if name in self: 402 return self.get(name) 403 404 raise ApiAttributeError( 405 "{0} has no attribute '{1}'".format(type(self).__name__, name), [e for e in [self._path_to_item, name] if e] 406 ) 407 408 def __contains__(self, name): 409 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 410 if name in self.required_properties: 411 return name in self.__dict__ 412 413 return name in self.__dict__["_data_store"] 414 415 def to_str(self): 416 """Returns the string representation of the model""" 417 return str(self.value) 418 419 def __eq__(self, other): 420 """Returns true if both objects are equal""" 421 if not isinstance(other, self.__class__): 422 return False 423 424 this_val = self._data_store["value"] 425 that_val = other._data_store["value"] 426 types = set() 427 types.add(this_val.__class__) 428 types.add(that_val.__class__) 429 vals_equal = this_val == that_val 430 return vals_equal 431 432 433class ModelNormal(OpenApiModel): 434 """the parent class of models whose type == object in their 435 swagger/openapi""" 436 437 def __setitem__(self, name, value): 438 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 439 if name in self.required_properties: 440 self.__dict__[name] = value 441 return 442 443 self.set_attribute(name, value) 444 445 def get(self, name, default=None): 446 """returns the value of an attribute or some default value if the attribute was not set""" 447 if name in self.required_properties: 448 return self.__dict__[name] 449 450 return self.__dict__["_data_store"].get(name, default) 451 452 def __getitem__(self, name): 453 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 454 if name in self: 455 return self.get(name) 456 457 raise ApiAttributeError( 458 "{0} has no attribute '{1}'".format(type(self).__name__, name), [e for e in [self._path_to_item, name] if e] 459 ) 460 461 def __contains__(self, name): 462 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 463 if name in self.required_properties: 464 return name in self.__dict__ 465 466 return name in self.__dict__["_data_store"] 467 468 def to_dict(self): 469 """Returns the model properties as a dict""" 470 return model_to_dict(self, serialize=False) 471 472 def to_str(self): 473 """Returns the string representation of the model""" 474 return pprint.pformat(self.to_dict()) 475 476 def __eq__(self, other): 477 """Returns true if both objects are equal""" 478 if not isinstance(other, self.__class__): 479 return False 480 481 if not set(self._data_store.keys()) == set(other._data_store.keys()): 482 return False 483 for _var_name, this_val in self._data_store.items(): 484 that_val = other._data_store[_var_name] 485 types = set() 486 types.add(this_val.__class__) 487 types.add(that_val.__class__) 488 vals_equal = this_val == that_val 489 if not vals_equal: 490 return False 491 return True 492 493 494class ModelComposed(OpenApiModel): 495 """the parent class of models whose type == object in their 496 swagger/openapi and have oneOf/allOf/anyOf 497 498 When one sets a property we use var_name_to_model_instances to store the value in 499 the correct class instances + run any type checking + validation code. 500 When one gets a property we use var_name_to_model_instances to get the value 501 from the correct class instances. 502 This allows multiple composed schemas to contain the same property with additive 503 constraints on the value. 504 505 _composed_schemas (dict) stores the anyOf/allOf/oneOf classes 506 key (str): allOf/oneOf/anyOf 507 value (list): the classes in the XOf definition. 508 Note: none_type can be included when the openapi document version >= 3.1.0 509 _composed_instances (list): stores a list of instances of the composed schemas 510 defined in _composed_schemas. When properties are accessed in the self instance, 511 they are returned from the self._data_store or the data stores in the instances 512 in self._composed_schemas 513 _var_name_to_model_instances (dict): maps between a variable name on self and 514 the composed instances (self included) which contain that data 515 key (str): property name 516 value (list): list of class instances, self or instances in _composed_instances 517 which contain the value that the key is referring to. 518 """ 519 520 def __setitem__(self, name, value): 521 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 522 if name in self.required_properties: 523 self.__dict__[name] = value 524 return 525 526 """ 527 Use cases: 528 1. additional_properties_type is None (additionalProperties == False in spec) 529 Check for property presence in self.openapi_types 530 if not present then throw an error 531 if present set in self, set attribute 532 always set on composed schemas 533 2. additional_properties_type exists 534 set attribute on self 535 always set on composed schemas 536 """ 537 if self.additional_properties_type is None: 538 """ 539 For an attribute to exist on a composed schema it must: 540 - fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND 541 - fulfill schema_requirements in each oneOf/anyOf/allOf schemas 542 543 schema_requirements: 544 For an attribute to exist on a schema it must: 545 - be present in properties at the schema OR 546 - have additionalProperties unset (defaults additionalProperties = any type) OR 547 - have additionalProperties set 548 """ 549 if name not in self.openapi_types: 550 raise ApiAttributeError( 551 "{0} has no attribute '{1}'".format(type(self).__name__, name), 552 [e for e in [self._path_to_item, name] if e], 553 ) 554 # attribute must be set on self and composed instances 555 self.set_attribute(name, value) 556 for model_instance in self._composed_instances: 557 setattr(model_instance, name, value) 558 if name not in self._var_name_to_model_instances: 559 # we assigned an additional property 560 self.__dict__["_var_name_to_model_instances"][name] = self._composed_instances + [self] 561 return None 562 563 __unset_attribute_value__ = object() 564 565 def get(self, name, default=None): 566 """returns the value of an attribute or some default value if the attribute was not set""" 567 if name in self.required_properties: 568 return self.__dict__[name] 569 570 # get the attribute from the correct instance 571 model_instances = self._var_name_to_model_instances.get(name) 572 values = [] 573 # A composed model stores self and child (oneof/anyOf/allOf) models under 574 # self._var_name_to_model_instances. 575 # Any property must exist in self and all model instances 576 # The value stored in all model instances must be the same 577 if model_instances: 578 for model_instance in model_instances: 579 if name in model_instance._data_store: 580 v = model_instance._data_store[name] 581 if v not in values: 582 values.append(v) 583 len_values = len(values) 584 if len_values == 0: 585 return default 586 elif len_values == 1: 587 return values[0] 588 elif len_values > 1: 589 raise ApiValueError( 590 "Values stored for property {0} in {1} differ when looking " 591 "at self and self's composed instances. All values must be " 592 "the same".format(name, type(self).__name__), 593 [e for e in [self._path_to_item, name] if e], 594 ) 595 596 def __getitem__(self, name): 597 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 598 value = self.get(name, self.__unset_attribute_value__) 599 if value is self.__unset_attribute_value__: 600 raise ApiAttributeError( 601 "{0} has no attribute '{1}'".format(type(self).__name__, name), 602 [e for e in [self._path_to_item, name] if e], 603 ) 604 return value 605 606 def __contains__(self, name): 607 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 608 609 if name in self.required_properties: 610 return name in self.__dict__ 611 612 model_instances = self._var_name_to_model_instances.get(name, self._additional_properties_model_instances) 613 614 if model_instances: 615 for model_instance in model_instances: 616 if name in model_instance._data_store: 617 return True 618 619 return False 620 621 def to_dict(self): 622 """Returns the model properties as a dict""" 623 return model_to_dict(self, serialize=False) 624 625 def to_str(self): 626 """Returns the string representation of the model""" 627 return pprint.pformat(self.to_dict()) 628 629 def __eq__(self, other): 630 """Returns true if both objects are equal""" 631 if not isinstance(other, self.__class__): 632 return False 633 634 if not set(self._data_store.keys()) == set(other._data_store.keys()): 635 return False 636 for _var_name, this_val in self._data_store.items(): 637 that_val = other._data_store[_var_name] 638 types = set() 639 types.add(this_val.__class__) 640 types.add(that_val.__class__) 641 vals_equal = this_val == that_val 642 if not vals_equal: 643 return False 644 return True 645 646 647COERCION_INDEX_BY_TYPE = { 648 ModelComposed: 0, 649 ModelNormal: 1, 650 ModelSimple: 2, 651 none_type: 3, # The type of 'None'. 652 list: 4, 653 dict: 5, 654 float: 6, 655 int: 7, 656 bool: 8, 657 datetime: 9, 658 date: 10, 659 str: 11, 660 file_type: 12, # 'file_type' is an alias for the built-in 'file' or 'io.IOBase' type. 661} 662 663# these are used to limit what type conversions we try to do 664# when we have a valid type already and we want to try converting 665# to another type 666UPCONVERSION_TYPE_PAIRS = ( 667 (str, datetime), 668 (str, date), 669 (int, float), # A float may be serialized as an integer, e.g. '3' is a valid serialized float. 670 (list, ModelComposed), 671 (dict, ModelComposed), 672 (str, ModelComposed), 673 (int, ModelComposed), 674 (float, ModelComposed), 675 (list, ModelComposed), 676 (list, ModelNormal), 677 (dict, ModelNormal), 678 (str, ModelSimple), 679 (int, ModelSimple), 680 (float, ModelSimple), 681 (list, ModelSimple), 682) 683 684COERCIBLE_TYPE_PAIRS = { 685 False: ( # client instantiation of a model with client data 686 # (dict, ModelComposed), 687 # (list, ModelComposed), 688 # (dict, ModelNormal), 689 # (list, ModelNormal), 690 # (str, ModelSimple), 691 # (int, ModelSimple), 692 # (float, ModelSimple), 693 # (list, ModelSimple), 694 # (str, int), 695 # (str, float), 696 # (str, datetime), 697 # (str, date), 698 # (int, str), 699 # (float, str), 700 ), 701 True: ( # server -> client data 702 (dict, ModelComposed), 703 (list, ModelComposed), 704 (dict, ModelNormal), 705 (list, ModelNormal), 706 (str, ModelSimple), 707 (int, ModelSimple), 708 (float, ModelSimple), 709 (list, ModelSimple), 710 # (str, int), 711 # (str, float), 712 (str, datetime), 713 (str, date), 714 # (int, str), 715 # (float, str), 716 (str, file_type), 717 ), 718} 719 720 721def get_simple_class(input_value): 722 """Returns an input_value's simple class that we will use for type checking 723 Python2: 724 float and int will return int, where int is the python3 int backport 725 str and unicode will return str, where str is the python3 str backport 726 Note: float and int ARE both instances of int backport 727 Note: str_py2 and unicode_py2 are NOT both instances of str backport 728 729 Args: 730 input_value (class/class_instance): the item for which we will return 731 the simple class 732 """ 733 if isinstance(input_value, type): 734 # input_value is a class 735 return input_value 736 elif isinstance(input_value, tuple): 737 return tuple 738 elif isinstance(input_value, list): 739 return list 740 elif isinstance(input_value, dict): 741 return dict 742 elif isinstance(input_value, none_type): 743 return none_type 744 elif isinstance(input_value, file_type): 745 return file_type 746 elif isinstance(input_value, bool): 747 # this must be higher than the int check because 748 # isinstance(True, int) == True 749 return bool 750 elif isinstance(input_value, int): 751 return int 752 elif isinstance(input_value, datetime): 753 # this must be higher than the date check because 754 # isinstance(datetime_instance, date) == True 755 return datetime 756 elif isinstance(input_value, date): 757 return date 758 elif isinstance(input_value, str): 759 return str 760 return type(input_value) 761 762 763def check_allowed_values(allowed_values, input_variable_path, input_values): 764 """Raises an exception if the input_values are not allowed 765 766 Args: 767 allowed_values (dict): the allowed_values dict 768 input_variable_path (tuple): the path to the input variable 769 input_values (list/str/int/float/date/datetime): the values that we 770 are checking to see if they are in allowed_values 771 """ 772 these_allowed_values = list(allowed_values[input_variable_path].values()) 773 if isinstance(input_values, list) and not set(input_values).issubset(set(these_allowed_values)): 774 invalid_values = (", ".join(map(str, set(input_values) - set(these_allowed_values))),) 775 raise ApiValueError( 776 "Invalid values for `%s` [%s], must be a subset of [%s]" 777 % (input_variable_path[0], invalid_values, ", ".join(map(str, these_allowed_values))) 778 ) 779 elif isinstance(input_values, dict) and not set(input_values.keys()).issubset(set(these_allowed_values)): 780 invalid_values = ", ".join(map(str, set(input_values.keys()) - set(these_allowed_values))) 781 raise ApiValueError( 782 "Invalid keys in `%s` [%s], must be a subset of [%s]" 783 % (input_variable_path[0], invalid_values, ", ".join(map(str, these_allowed_values))) 784 ) 785 elif not isinstance(input_values, (list, dict)) and input_values not in these_allowed_values: 786 raise ApiValueError( 787 "Invalid value for `%s` (%s), must be one of %s" 788 % (input_variable_path[0], input_values, these_allowed_values) 789 ) 790 791 792def is_json_validation_enabled(schema_keyword, configuration=None): 793 """Returns true if JSON schema validation is enabled for the specified 794 validation keyword. This can be used to skip JSON schema structural validation 795 as requested in the configuration. 796 797 Args: 798 schema_keyword (string): the name of a JSON schema validation keyword. 799 configuration (Configuration): the configuration class. 800 """ 801 802 return ( 803 configuration is None 804 or not hasattr(configuration, "_disabled_client_side_validations") 805 or schema_keyword not in configuration._disabled_client_side_validations 806 ) 807 808 809def check_validations(validations, input_variable_path, input_values, configuration=None): 810 """Raises an exception if the input_values are invalid 811 812 Args: 813 validations (dict): the validation dictionary. 814 input_variable_path (tuple): the path to the input variable. 815 input_values (list/str/int/float/date/datetime): the values that we 816 are checking. 817 configuration (Configuration): the configuration class. 818 """ 819 820 if input_values is None: 821 return 822 823 current_validations = validations[input_variable_path] 824 if ( 825 is_json_validation_enabled("multipleOf", configuration) 826 and "multiple_of" in current_validations 827 and isinstance(input_values, (int, float)) 828 and not (float(input_values) / current_validations["multiple_of"]).is_integer() 829 ): 830 # Note 'multipleOf' will be as good as the floating point arithmetic. 831 raise ApiValueError( 832 "Invalid value for `%s`, value must be a multiple of " 833 "`%s`" % (input_variable_path[0], current_validations["multiple_of"]) 834 ) 835 836 if ( 837 is_json_validation_enabled("maxLength", configuration) 838 and "max_length" in current_validations 839 and len(input_values) > current_validations["max_length"] 840 ): 841 raise ApiValueError( 842 "Invalid value for `%s`, length must be less than or equal to " 843 "`%s`" % (input_variable_path[0], current_validations["max_length"]) 844 ) 845 846 if ( 847 is_json_validation_enabled("minLength", configuration) 848 and "min_length" in current_validations 849 and len(input_values) < current_validations["min_length"] 850 ): 851 raise ApiValueError( 852 "Invalid value for `%s`, length must be greater than or equal to " 853 "`%s`" % (input_variable_path[0], current_validations["min_length"]) 854 ) 855 856 if ( 857 is_json_validation_enabled("maxItems", configuration) 858 and "max_items" in current_validations 859 and len(input_values) > current_validations["max_items"] 860 ): 861 raise ApiValueError( 862 "Invalid value for `%s`, number of items must be less than or " 863 "equal to `%s`" % (input_variable_path[0], current_validations["max_items"]) 864 ) 865 866 if ( 867 is_json_validation_enabled("minItems", configuration) 868 and "min_items" in current_validations 869 and len(input_values) < current_validations["min_items"] 870 ): 871 raise ValueError( 872 "Invalid value for `%s`, number of items must be greater than or " 873 "equal to `%s`" % (input_variable_path[0], current_validations["min_items"]) 874 ) 875 876 items = ("exclusive_maximum", "inclusive_maximum", "exclusive_minimum", "inclusive_minimum") 877 if any(item in current_validations for item in items): 878 if isinstance(input_values, list): 879 max_val = max(input_values) 880 min_val = min(input_values) 881 elif isinstance(input_values, dict): 882 max_val = max(input_values.values()) 883 min_val = min(input_values.values()) 884 else: 885 max_val = input_values 886 min_val = input_values 887 888 if ( 889 is_json_validation_enabled("exclusiveMaximum", configuration) 890 and "exclusive_maximum" in current_validations 891 and max_val >= current_validations["exclusive_maximum"] 892 ): 893 raise ApiValueError( 894 "Invalid value for `%s`, must be a value less than `%s`" 895 % (input_variable_path[0], current_validations["exclusive_maximum"]) 896 ) 897 898 if ( 899 is_json_validation_enabled("maximum", configuration) 900 and "inclusive_maximum" in current_validations 901 and max_val > current_validations["inclusive_maximum"] 902 ): 903 raise ApiValueError( 904 "Invalid value for `%s`, must be a value less than or equal to " 905 "`%s`" % (input_variable_path[0], current_validations["inclusive_maximum"]) 906 ) 907 908 if ( 909 is_json_validation_enabled("exclusiveMinimum", configuration) 910 and "exclusive_minimum" in current_validations 911 and min_val <= current_validations["exclusive_minimum"] 912 ): 913 raise ApiValueError( 914 "Invalid value for `%s`, must be a value greater than `%s`" 915 % (input_variable_path[0], current_validations["exclusive_maximum"]) 916 ) 917 918 if ( 919 is_json_validation_enabled("minimum", configuration) 920 and "inclusive_minimum" in current_validations 921 and min_val < current_validations["inclusive_minimum"] 922 ): 923 raise ApiValueError( 924 "Invalid value for `%s`, must be a value greater than or equal " 925 "to `%s`" % (input_variable_path[0], current_validations["inclusive_minimum"]) 926 ) 927 flags = current_validations.get("regex", {}).get("flags", 0) 928 if ( 929 is_json_validation_enabled("pattern", configuration) 930 and "regex" in current_validations 931 and not re.search(current_validations["regex"]["pattern"], input_values, flags=flags) 932 ): 933 err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( 934 input_variable_path[0], 935 current_validations["regex"]["pattern"], 936 ) 937 if flags != 0: 938 # Don't print the regex flags if the flags are not 939 # specified in the OAS document. 940 err_msg = r"%s with flags=`%s`" % (err_msg, flags) 941 raise ApiValueError(err_msg) 942 943 944def order_response_types(required_types): 945 """Returns the required types sorted in coercion order 946 947 Args: 948 required_types (list/tuple): collection of classes or instance of 949 list or dict with class information inside it. 950 951 Returns: 952 (list): coercion order sorted collection of classes or instance 953 of list or dict with class information inside it. 954 """ 955 956 def index_getter(class_or_instance): 957 if isinstance(class_or_instance, list): 958 return COERCION_INDEX_BY_TYPE[list] 959 elif isinstance(class_or_instance, dict): 960 return COERCION_INDEX_BY_TYPE[dict] 961 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelComposed): 962 return COERCION_INDEX_BY_TYPE[ModelComposed] 963 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelNormal): 964 return COERCION_INDEX_BY_TYPE[ModelNormal] 965 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelSimple): 966 return COERCION_INDEX_BY_TYPE[ModelSimple] 967 elif class_or_instance in COERCION_INDEX_BY_TYPE: 968 return COERCION_INDEX_BY_TYPE[class_or_instance] 969 raise ApiValueError("Unsupported type: %s" % class_or_instance) 970 971 sorted_types = sorted(required_types, key=lambda class_or_instance: index_getter(class_or_instance)) 972 return sorted_types 973 974 975def remove_uncoercible(required_types_classes, current_item, spec_property_naming, must_convert=True): 976 """Only keeps the type conversions that are possible 977 978 Args: 979 required_types_classes (tuple): tuple of classes that are required 980 these should be ordered by COERCION_INDEX_BY_TYPE 981 spec_property_naming (bool): True if the variable names in the input 982 data are serialized names as specified in the OpenAPI document. 983 False if the variables names in the input data are python 984 variable names in PEP-8 snake case. 985 current_item (any): the current item (input data) to be converted 986 987 Keyword Args: 988 must_convert (bool): if True the item to convert is of the wrong 989 type and we want a big list of coercibles 990 if False, we want a limited list of coercibles 991 992 Returns: 993 (list): the remaining coercible required types, classes only 994 """ 995 current_type_simple = get_simple_class(current_item) 996 997 results_classes = [] 998 for required_type_class in required_types_classes: 999 # convert our models to OpenApiModel 1000 required_type_class_simplified = required_type_class 1001 if isinstance(required_type_class_simplified, type): 1002 if issubclass(required_type_class_simplified, ModelComposed): 1003 required_type_class_simplified = ModelComposed 1004 elif issubclass(required_type_class_simplified, ModelNormal): 1005 required_type_class_simplified = ModelNormal 1006 elif issubclass(required_type_class_simplified, ModelSimple): 1007 required_type_class_simplified = ModelSimple 1008 1009 if required_type_class_simplified == current_type_simple: 1010 # don't consider converting to one's own class 1011 continue 1012 1013 class_pair = (current_type_simple, required_type_class_simplified) 1014 if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: 1015 results_classes.append(required_type_class) 1016 elif class_pair in UPCONVERSION_TYPE_PAIRS: 1017 results_classes.append(required_type_class) 1018 return results_classes 1019 1020 1021def get_discriminated_classes(cls): 1022 """ 1023 Returns all the classes that a discriminator converts to 1024 TODO: lru_cache this 1025 """ 1026 possible_classes = [] 1027 key = list(cls.discriminator.keys())[0] 1028 if is_type_nullable(cls): 1029 possible_classes.append(cls) 1030 for discr_cls in cls.discriminator[key].values(): 1031 if hasattr(discr_cls, "discriminator") and discr_cls.discriminator is not None: 1032 possible_classes.extend(get_discriminated_classes(discr_cls)) 1033 else: 1034 possible_classes.append(discr_cls) 1035 return possible_classes 1036 1037 1038def get_possible_classes(cls, from_server_context): 1039 # TODO: lru_cache this 1040 possible_classes = [cls] 1041 if from_server_context: 1042 return possible_classes 1043 if hasattr(cls, "discriminator") and cls.discriminator is not None: 1044 possible_classes = [] 1045 possible_classes.extend(get_discriminated_classes(cls)) 1046 elif issubclass(cls, ModelComposed): 1047 possible_classes.extend(composed_model_input_classes(cls)) 1048 return possible_classes 1049 1050 1051def get_required_type_classes(required_types_mixed, spec_property_naming): 1052 """Converts the tuple required_types into a tuple and a dict described 1053 below 1054 1055 Args: 1056 required_types_mixed (tuple/list): will contain either classes or 1057 instance of list or dict 1058 spec_property_naming (bool): if True these values came from the 1059 server, and we use the data types in our endpoints. 1060 If False, we are client side and we need to include 1061 oneOf and discriminator classes inside the data types in our endpoints 1062 1063 Returns: 1064 (valid_classes, dict_valid_class_to_child_types_mixed): 1065 valid_classes (tuple): the valid classes that the current item 1066 should be 1067 dict_valid_class_to_child_types_mixed (dict): 1068 valid_class (class): this is the key 1069 child_types_mixed (list/dict/tuple): describes the valid child 1070 types 1071 """ 1072 valid_classes = [] 1073 child_req_types_by_current_type = {} 1074 for required_type in required_types_mixed: 1075 if isinstance(required_type, list): 1076 valid_classes.append(list) 1077 child_req_types_by_current_type[list] = required_type 1078 elif isinstance(required_type, tuple): 1079 valid_classes.append(tuple) 1080 child_req_types_by_current_type[tuple] = required_type 1081 elif isinstance(required_type, dict): 1082 valid_classes.append(dict) 1083 child_req_types_by_current_type[dict] = required_type[str] 1084 else: 1085 valid_classes.extend(get_possible_classes(required_type, spec_property_naming)) 1086 return tuple(valid_classes), child_req_types_by_current_type 1087 1088 1089def change_keys_js_to_python(input_dict, model_class): 1090 """ 1091 Converts from javascript_key keys in the input_dict to python_keys in 1092 the output dict using the mapping in model_class. 1093 If the input_dict contains a key which does not declared in the model_class, 1094 the key is added to the output dict as is. The assumption is the model_class 1095 may have undeclared properties (additionalProperties attribute in the OAS 1096 document). 1097 """ 1098 1099 if getattr(model_class, "attribute_map", None) is None: 1100 return input_dict 1101 output_dict = {} 1102 reversed_attr_map = {value: key for key, value in model_class.attribute_map.items()} 1103 for javascript_key, value in input_dict.items(): 1104 python_key = reversed_attr_map.get(javascript_key) 1105 if python_key is None: 1106 # if the key is unknown, it is in error or it is an 1107 # additionalProperties variable 1108 python_key = javascript_key 1109 output_dict[python_key] = value 1110 return output_dict 1111 1112 1113def get_type_error(var_value, path_to_item, valid_classes, key_type=False): 1114 error_msg = type_error_message( 1115 var_name=path_to_item[-1], var_value=var_value, valid_classes=valid_classes, key_type=key_type 1116 ) 1117 return ApiTypeError(error_msg, path_to_item=path_to_item, valid_classes=valid_classes, key_type=key_type) 1118 1119 1120def deserialize_primitive(data, klass, path_to_item): 1121 """Deserializes string to primitive type. 1122 1123 :param data: str/int/float 1124 :param klass: str/class the class to convert to 1125 1126 :return: int, float, str, bool, date, datetime 1127 """ 1128 additional_message = "" 1129 try: 1130 if klass in {datetime, date}: 1131 additional_message = ( 1132 "If you need your parameter to have a fallback " 1133 "string value, please set its type as `type: {}` in your " 1134 "spec. That allows the value to be any type. " 1135 ) 1136 if klass == datetime: 1137 if len(data) < 8: 1138 raise ValueError("This is not a datetime") 1139 # The string should be in iso8601 datetime format. 1140 parsed_datetime = parse(data) 1141 date_only = ( 1142 parsed_datetime.hour == 0 1143 and parsed_datetime.minute == 0 1144 and parsed_datetime.second == 0 1145 and parsed_datetime.tzinfo is None 1146 and 8 <= len(data) <= 10 1147 ) 1148 if date_only: 1149 raise ValueError("This is a date, not a datetime") 1150 return parsed_datetime 1151 elif klass == date: 1152 if len(data) < 8: 1153 raise ValueError("This is not a date") 1154 return parse(data).date() 1155 else: 1156 converted_value = klass(data) 1157 if isinstance(data, str) and klass == float: 1158 if str(converted_value) != data: 1159 # '7' -> 7.0 -> '7.0' != '7' 1160 raise ValueError("This is not a float") 1161 return converted_value 1162 except (OverflowError, ValueError) as ex: 1163 # parse can raise OverflowError 1164 raise ApiValueError( 1165 "{0}Failed to parse {1} as {2}".format(additional_message, repr(data), klass.__name__), 1166 path_to_item=path_to_item, 1167 ) from ex 1168 1169 1170def get_discriminator_class(model_class, discr_name, discr_value, cls_visited): 1171 """Returns the child class specified by the discriminator. 1172 1173 Args: 1174 model_class (OpenApiModel): the model class. 1175 discr_name (string): the name of the discriminator property. 1176 discr_value (any): the discriminator value. 1177 cls_visited (list): list of model classes that have been visited. 1178 Used to determine the discriminator class without 1179 visiting circular references indefinitely. 1180 1181 Returns: 1182 used_model_class (class/None): the chosen child class that will be used 1183 to deserialize the data, for example dog.Dog. 1184 If a class is not found, None is returned. 1185 """ 1186 1187 if model_class in cls_visited: 1188 # The class has already been visited and no suitable class was found. 1189 return None 1190 cls_visited.append(model_class) 1191 used_model_class = None 1192 if discr_name in model_class.discriminator: 1193 class_name_to_discr_class = model_class.discriminator[discr_name] 1194 used_model_class = class_name_to_discr_class.get(discr_value) 1195 if used_model_class is None: 1196 # We didn't find a discriminated class in class_name_to_discr_class. 1197 # So look in the ancestor or descendant discriminators 1198 # The discriminator mapping may exist in a descendant (anyOf, oneOf) 1199 # or ancestor (allOf). 1200 # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat 1201 # hierarchy, the discriminator mappings may be defined at any level 1202 # in the hierarchy. 1203 # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig 1204 # if we try to make BasquePig from mammal, we need to travel through 1205 # the oneOf descendant discriminators to find BasquePig 1206 descendant_classes = model_class._composed_schemas.get("oneOf", ()) + model_class._composed_schemas.get( 1207 "anyOf", () 1208 ) 1209 ancestor_classes = model_class._composed_schemas.get("allOf", ()) 1210 possible_classes = descendant_classes + ancestor_classes 1211 for cls in possible_classes: 1212 # Check if the schema has inherited discriminators. 1213 if hasattr(cls, "discriminator") and cls.discriminator is not None: 1214 used_model_class = get_discriminator_class(cls, discr_name, discr_value, cls_visited) 1215 if used_model_class is not None: 1216 return used_model_class 1217 return used_model_class 1218 1219 1220def deserialize_model(model_data, model_class, path_to_item, check_type, configuration, spec_property_naming): 1221 """Deserializes model_data to model instance. 1222 1223 Args: 1224 model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model 1225 model_class (OpenApiModel): the model class 1226 path_to_item (list): path to the model in the received data 1227 check_type (bool): whether to check the data tupe for the values in 1228 the model 1229 configuration (Configuration): the instance to use to convert files 1230 spec_property_naming (bool): True if the variable names in the input 1231 data are serialized names as specified in the OpenAPI document. 1232 False if the variables names in the input data are python 1233 variable names in PEP-8 snake case. 1234 1235 Returns: 1236 model instance 1237 1238 Raise: 1239 ApiTypeError 1240 ApiValueError 1241 ApiKeyError 1242 """ 1243 1244 kw_args = dict( 1245 _check_type=check_type, 1246 _path_to_item=path_to_item, 1247 _configuration=configuration, 1248 _spec_property_naming=spec_property_naming, 1249 ) 1250 1251 if issubclass(model_class, ModelSimple): 1252 return model_class._new_from_openapi_data(model_data, **kw_args) 1253 elif isinstance(model_data, list): 1254 return model_class._new_from_openapi_data(*model_data, **kw_args) 1255 if isinstance(model_data, dict): 1256 kw_args.update(model_data) 1257 return model_class._new_from_openapi_data(**kw_args) 1258 elif isinstance(model_data, PRIMITIVE_TYPES): 1259 return model_class._new_from_openapi_data(model_data, **kw_args) 1260 1261 1262def deserialize_file(response_data, configuration, content_disposition=None): 1263 """Deserializes body to file 1264 1265 Saves response body into a file in a temporary folder, 1266 using the filename from the `Content-Disposition` header if provided. 1267 1268 Args: 1269 param response_data (str): the file data to write 1270 configuration (Configuration): the instance to use to convert files 1271 1272 Keyword Args: 1273 content_disposition (str): the value of the Content-Disposition 1274 header 1275 1276 Returns: 1277 (file_type): the deserialized file which is open 1278 The user is responsible for closing and reading the file 1279 """ 1280 fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) 1281 os.close(fd) 1282 os.remove(path) 1283 1284 if content_disposition: 1285 filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition).group(1) 1286 path = os.path.join(os.path.dirname(path), filename) 1287 1288 with open(path, "wb") as f: 1289 if isinstance(response_data, str): 1290 # change str to bytes so we can write it 1291 response_data = response_data.encode("utf-8") 1292 f.write(response_data) 1293 1294 f = open(path, "rb") 1295 return f 1296 1297 1298def attempt_convert_item( 1299 input_value, 1300 valid_classes, 1301 path_to_item, 1302 configuration, 1303 spec_property_naming, 1304 key_type=False, 1305 must_convert=False, 1306 check_type=True, 1307): 1308 """ 1309 Args: 1310 input_value (any): the data to convert 1311 valid_classes (any): the classes that are valid 1312 path_to_item (list): the path to the item to convert 1313 configuration (Configuration): the instance to use to convert files 1314 spec_property_naming (bool): True if the variable names in the input 1315 data are serialized names as specified in the OpenAPI document. 1316 False if the variables names in the input data are python 1317 variable names in PEP-8 snake case. 1318 key_type (bool): if True we need to convert a key type (not supported) 1319 must_convert (bool): if True we must convert 1320 check_type (bool): if True we check the type or the returned data in 1321 ModelComposed/ModelNormal/ModelSimple instances 1322 1323 Returns: 1324 instance (any) the fixed item 1325 1326 Raises: 1327 ApiTypeError 1328 ApiValueError 1329 ApiKeyError 1330 """ 1331 valid_classes_ordered = order_response_types(valid_classes) 1332 valid_classes_coercible = remove_uncoercible(valid_classes_ordered, input_value, spec_property_naming) 1333 if not valid_classes_coercible or key_type: 1334 # we do not handle keytype errors, json will take care 1335 # of this for us 1336 if configuration is None or not configuration.discard_unknown_keys: 1337 raise get_type_error(input_value, path_to_item, valid_classes, key_type=key_type) 1338 for valid_class in valid_classes_coercible: 1339 try: 1340 if issubclass(valid_class, OpenApiModel): 1341 return deserialize_model( 1342 input_value, valid_class, path_to_item, check_type, configuration, spec_property_naming 1343 ) 1344 elif valid_class == file_type: 1345 return deserialize_file(input_value, configuration) 1346 return deserialize_primitive(input_value, valid_class, path_to_item) 1347 except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: 1348 if must_convert: 1349 raise conversion_exc 1350 # if we have conversion errors when must_convert == False 1351 # we ignore the exception and move on to the next class 1352 continue 1353 # we were unable to convert, must_convert == False 1354 return input_value 1355 1356 1357def is_type_nullable(input_type): 1358 """ 1359 Returns true if None is an allowed value for the specified input_type. 1360 1361 A type is nullable if at least one of the following conditions is true: 1362 1. The OAS 'nullable' attribute has been specified, 1363 1. The type is the 'null' type, 1364 1. The type is a anyOf/oneOf composed schema, and a child schema is 1365 the 'null' type. 1366 Args: 1367 input_type (type): the class of the input_value that we are 1368 checking 1369 Returns: 1370 bool 1371 """ 1372 if input_type is none_type: 1373 return True 1374 if issubclass(input_type, OpenApiModel) and input_type._nullable: 1375 return True 1376 if issubclass(input_type, ModelComposed): 1377 # If oneOf/anyOf, check if the 'null' type is one of the allowed types. 1378 for t in input_type._composed_schemas.get("oneOf", ()): 1379 if is_type_nullable(t): 1380 return True 1381 for t in input_type._composed_schemas.get("anyOf", ()): 1382 if is_type_nullable(t): 1383 return True 1384 return False 1385 1386 1387def is_valid_type(input_class_simple, valid_classes): 1388 """ 1389 Args: 1390 input_class_simple (class): the class of the input_value that we are 1391 checking 1392 valid_classes (tuple): the valid classes that the current item 1393 should be 1394 Returns: 1395 bool 1396 """ 1397 valid_type = input_class_simple in valid_classes 1398 if not valid_type and (issubclass(input_class_simple, OpenApiModel) or input_class_simple is none_type): 1399 for valid_class in valid_classes: 1400 if input_class_simple is none_type and is_type_nullable(valid_class): 1401 # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. 1402 return True 1403 if not (issubclass(valid_class, OpenApiModel) and valid_class.discriminator): 1404 continue 1405 discr_propertyname_py = list(valid_class.discriminator.keys())[0] 1406 discriminator_classes = valid_class.discriminator[discr_propertyname_py].values() 1407 valid_type = is_valid_type(input_class_simple, discriminator_classes) 1408 if valid_type: 1409 return True 1410 return valid_type 1411 1412 1413def validate_and_convert_types( 1414 input_value, required_types_mixed, path_to_item, spec_property_naming, _check_type, configuration=None 1415): 1416 """Raises a TypeError is there is a problem, otherwise returns value 1417 1418 Args: 1419 input_value (any): the data to validate/convert 1420 required_types_mixed (list/dict/tuple): A list of 1421 valid classes, or a list tuples of valid classes, or a dict where 1422 the value is a tuple of value classes 1423 path_to_item: (list) the path to the data being validated 1424 this stores a list of keys or indices to get to the data being 1425 validated 1426 spec_property_naming (bool): True if the variable names in the input 1427 data are serialized names as specified in the OpenAPI document. 1428 False if the variables names in the input data are python 1429 variable names in PEP-8 snake case. 1430 _check_type: (boolean) if true, type will be checked and conversion 1431 will be attempted. 1432 configuration: (Configuration): the configuration class to use 1433 when converting file_type items. 1434 If passed, conversion will be attempted when possible 1435 If not passed, no conversions will be attempted and 1436 exceptions will be raised 1437 1438 Returns: 1439 the correctly typed value 1440 1441 Raises: 1442 ApiTypeError 1443 """ 1444 results = get_required_type_classes(required_types_mixed, spec_property_naming) 1445 valid_classes, child_req_types_by_current_type = results 1446 1447 input_class_simple = get_simple_class(input_value) 1448 valid_type = is_valid_type(input_class_simple, valid_classes) 1449 if not valid_type: 1450 if configuration: 1451 # if input_value is not valid_type try to convert it 1452 converted_instance = attempt_convert_item( 1453 input_value, 1454 valid_classes, 1455 path_to_item, 1456 configuration, 1457 spec_property_naming, 1458 key_type=False, 1459 must_convert=True, 1460 check_type=_check_type, 1461 ) 1462 return converted_instance 1463 else: 1464 raise get_type_error(input_value, path_to_item, valid_classes, key_type=False) 1465 1466 # input_value's type is in valid_classes 1467 if len(valid_classes) > 1 and configuration: 1468 # there are valid classes which are not the current class 1469 valid_classes_coercible = remove_uncoercible( 1470 valid_classes, input_value, spec_property_naming, must_convert=False 1471 ) 1472 if valid_classes_coercible: 1473 converted_instance = attempt_convert_item( 1474 input_value, 1475 valid_classes_coercible, 1476 path_to_item, 1477 configuration, 1478 spec_property_naming, 1479 key_type=False, 1480 must_convert=False, 1481 check_type=_check_type, 1482 ) 1483 return converted_instance 1484 1485 if child_req_types_by_current_type == {}: 1486 # all types are of the required types and there are no more inner 1487 # variables left to look at 1488 return input_value 1489 inner_required_types = child_req_types_by_current_type.get(type(input_value)) 1490 if inner_required_types is None: 1491 # for this type, there are not more inner variables left to look at 1492 return input_value 1493 if isinstance(input_value, list): 1494 if input_value == []: 1495 # allow an empty list 1496 return input_value 1497 for index, inner_value in enumerate(input_value): 1498 inner_path = list(path_to_item) 1499 inner_path.append(index) 1500 input_value[index] = validate_and_convert_types( 1501 inner_value, 1502 inner_required_types, 1503 inner_path, 1504 spec_property_naming, 1505 _check_type, 1506 configuration=configuration, 1507 ) 1508 elif isinstance(input_value, dict): 1509 if input_value == {}: 1510 # allow an empty dict 1511 return input_value 1512 for inner_key, inner_val in input_value.items(): 1513 inner_path = list(path_to_item) 1514 inner_path.append(inner_key) 1515 if get_simple_class(inner_key) != str: 1516 raise get_type_error(inner_key, inner_path, valid_classes, key_type=True) 1517 input_value[inner_key] = validate_and_convert_types( 1518 inner_val, 1519 inner_required_types, 1520 inner_path, 1521 spec_property_naming, 1522 _check_type, 1523 configuration=configuration, 1524 ) 1525 return input_value 1526 1527 1528def model_to_dict(model_instance, serialize=True): 1529 """Returns the model properties as a dict 1530 1531 Args: 1532 model_instance (one of your model instances): the model instance that 1533 will be converted to a dict. 1534 1535 Keyword Args: 1536 serialize (bool): if True, the keys in the dict will be values from 1537 attribute_map 1538 """ 1539 result = {} 1540 1541 model_instances = [model_instance] 1542 if model_instance._composed_schemas: 1543 model_instances.extend(model_instance._composed_instances) 1544 seen_json_attribute_names = set() 1545 used_fallback_python_attribute_names = set() 1546 py_to_json_map = {} 1547 for model_instance in model_instances: 1548 for attr, value in model_instance._data_store.items(): 1549 if serialize: 1550 # we use get here because additional property key names do not 1551 # exist in attribute_map 1552 try: 1553 attr = model_instance.attribute_map[attr] 1554 py_to_json_map.update(model_instance.attribute_map) 1555 seen_json_attribute_names.add(attr) 1556 except KeyError: 1557 used_fallback_python_attribute_names.add(attr) 1558 if isinstance(value, list): 1559 if not value: 1560 # empty list or None 1561 result[attr] = value 1562 else: 1563 res = [] 1564 for v in value: 1565 if isinstance(v, PRIMITIVE_TYPES) or v is None: 1566 res.append(v) 1567 elif isinstance(v, ModelSimple): 1568 res.append(v.value) 1569 else: 1570 res.append(model_to_dict(v, serialize=serialize)) 1571 result[attr] = res 1572 elif isinstance(value, dict): 1573 result[attr] = dict( 1574 map( 1575 lambda item: (item[0], model_to_dict(item[1], serialize=serialize)) 1576 if hasattr(item[1], "_data_store") 1577 else item, 1578 value.items(), 1579 ) 1580 ) 1581 elif isinstance(value, ModelSimple): 1582 result[attr] = value.value 1583 elif hasattr(value, "_data_store"): 1584 result[attr] = model_to_dict(value, serialize=serialize) 1585 else: 1586 result[attr] = value 1587 if serialize: 1588 for python_key in used_fallback_python_attribute_names: 1589 json_key = py_to_json_map.get(python_key) 1590 if json_key is None: 1591 continue 1592 if python_key == json_key: 1593 continue 1594 json_key_assigned_no_need_for_python_key = json_key in seen_json_attribute_names 1595 if json_key_assigned_no_need_for_python_key: 1596 del result[python_key] 1597 1598 return result 1599 1600 1601def type_error_message(var_value=None, var_name=None, valid_classes=None, key_type=None): 1602 """ 1603 Keyword Args: 1604 var_value (any): the variable which has the type_error 1605 var_name (str): the name of the variable which has the typ error 1606 valid_classes (tuple): the accepted classes for current_item's 1607 value 1608 key_type (bool): False if our value is a value in a dict 1609 True if it is a key in a dict 1610 False if our item is an item in a list 1611 """ 1612 key_or_value = "value" 1613 if key_type: 1614 key_or_value = "key" 1615 valid_classes_phrase = get_valid_classes_phrase(valid_classes) 1616 msg = "Invalid type for variable '{0}'. Required {1} type {2} and " "passed type was {3}".format( 1617 var_name, 1618 key_or_value, 1619 valid_classes_phrase, 1620 type(var_value).__name__, 1621 ) 1622 return msg 1623 1624 1625def get_valid_classes_phrase(input_classes): 1626 """Returns a string phrase describing what types are allowed""" 1627 all_classes = list(input_classes) 1628 all_classes = sorted(all_classes, key=lambda cls: cls.__name__) 1629 all_class_names = [cls.__name__ for cls in all_classes] 1630 if len(all_class_names) == 1: 1631 return "is {0}".format(all_class_names[0]) 1632 return "is one of [{0}]".format(", ".join(all_class_names)) 1633 1634 1635def get_allof_instances(self, model_args, constant_args): 1636 """ 1637 Args: 1638 self: the class we are handling 1639 model_args (dict): var_name to var_value 1640 used to make instances 1641 constant_args (dict): 1642 metadata arguments: 1643 _check_type 1644 _path_to_item 1645 _spec_property_naming 1646 _configuration 1647 _visited_composed_classes 1648 1649 Returns 1650 composed_instances (list) 1651 """ 1652 composed_instances = [] 1653 for allof_class in self._composed_schemas["allOf"]: 1654 try: 1655 allof_instance = allof_class(**model_args, **constant_args) 1656 composed_instances.append(allof_instance) 1657 except Exception as ex: 1658 raise ApiValueError( 1659 "Invalid inputs given to generate an instance of '%s'. The " 1660 "input data was invalid for the allOf schema '%s' in the composed " 1661 "schema '%s'. Error=%s" % (allof_class.__name__, allof_class.__name__, self.__class__.__name__, str(ex)) 1662 ) from ex 1663 return composed_instances 1664 1665 1666def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): 1667 """ 1668 Find the oneOf schema that matches the input data (e.g. payload). 1669 If exactly one schema matches the input data, an instance of that schema 1670 is returned. 1671 If zero or more than one schema match the input data, an exception is raised. 1672 In OAS 3.x, the payload MUST, by validation, match exactly one of the 1673 schemas described by oneOf. 1674 1675 Args: 1676 cls: the class we are handling 1677 model_kwargs (dict): var_name to var_value 1678 The input data, e.g. the payload that must match a oneOf schema 1679 in the OpenAPI document. 1680 constant_kwargs (dict): var_name to var_value 1681 args that every model requires, including configuration, server 1682 and path to item. 1683 1684 Kwargs: 1685 model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): 1686 the value to assign to a primitive class or ModelSimple class 1687 Notes: 1688 - this is only passed in when oneOf includes types which are not object 1689 - None is used to suppress handling of model_arg, nullable models are handled in __new__ 1690 1691 Returns 1692 oneof_instance (instance) 1693 """ 1694 if len(cls._composed_schemas["oneOf"]) == 0: 1695 return None 1696 1697 oneof_instances = [] 1698 # Iterate over each oneOf schema and determine if the input data 1699 # matches the oneOf schemas. 1700 for oneof_class in cls._composed_schemas["oneOf"]: 1701 # The composed oneOf schema allows the 'null' type and the input data 1702 # is the null value. This is a OAS >= 3.1 feature. 1703 if oneof_class is none_type: 1704 # skip none_types because we are deserializing dict data. 1705 # none_type deserialization is handled in the __new__ method 1706 continue 1707 1708 single_value_input = allows_single_value_input(oneof_class) 1709 1710 try: 1711 if not single_value_input: 1712 oneof_instance = oneof_class(**model_kwargs, **constant_kwargs) 1713 else: 1714 if issubclass(oneof_class, ModelSimple): 1715 oneof_instance = oneof_class(model_arg, **constant_kwargs) 1716 elif oneof_class in PRIMITIVE_TYPES: 1717 oneof_instance = validate_and_convert_types( 1718 model_arg, 1719 (oneof_class,), 1720 constant_kwargs["_path_to_item"], 1721 constant_kwargs["_spec_property_naming"], 1722 constant_kwargs["_check_type"], 1723 configuration=constant_kwargs["_configuration"], 1724 ) 1725 oneof_instances.append(oneof_instance) 1726 except Exception: 1727 pass 1728 if len(oneof_instances) == 0: 1729 raise ApiValueError( 1730 "Invalid inputs given to generate an instance of %s. None " 1731 "of the oneOf schemas matched the input data." % cls.__name__ 1732 ) 1733 elif len(oneof_instances) > 1: 1734 raise ApiValueError( 1735 "Invalid inputs given to generate an instance of %s. Multiple " 1736 "oneOf schemas matched the inputs, but a max of one is allowed." % cls.__name__ 1737 ) 1738 return oneof_instances[0] 1739 1740 1741def get_anyof_instances(self, model_args, constant_args): 1742 """ 1743 Args: 1744 self: the class we are handling 1745 model_args (dict): var_name to var_value 1746 The input data, e.g. the payload that must match at least one 1747 anyOf child schema in the OpenAPI document. 1748 constant_args (dict): var_name to var_value 1749 args that every model requires, including configuration, server 1750 and path to item. 1751 1752 Returns 1753 anyof_instances (list) 1754 """ 1755 anyof_instances = [] 1756 if len(self._composed_schemas["anyOf"]) == 0: 1757 return anyof_instances 1758 1759 for anyof_class in self._composed_schemas["anyOf"]: 1760 # The composed oneOf schema allows the 'null' type and the input data 1761 # is the null value. This is a OAS >= 3.1 feature. 1762 if anyof_class is none_type: 1763 # skip none_types because we are deserializing dict data. 1764 # none_type deserialization is handled in the __new__ method 1765 continue 1766 1767 try: 1768 anyof_instance = anyof_class(**model_args, **constant_args) 1769 anyof_instances.append(anyof_instance) 1770 except Exception: 1771 pass 1772 if len(anyof_instances) == 0: 1773 raise ApiValueError( 1774 "Invalid inputs given to generate an instance of %s. None of the " 1775 "anyOf schemas matched the inputs." % self.__class__.__name__ 1776 ) 1777 return anyof_instances 1778 1779 1780def get_discarded_args(self, composed_instances, model_args): 1781 """ 1782 Gathers the args that were discarded by configuration.discard_unknown_keys 1783 """ 1784 model_arg_keys = model_args.keys() 1785 discarded_args = set() 1786 # arguments passed to self were already converted to python names 1787 # before __init__ was called 1788 for instance in composed_instances: 1789 if instance.__class__ in self._composed_schemas["allOf"]: 1790 try: 1791 keys = instance.to_dict().keys() 1792 discarded_keys = model_args - keys 1793 discarded_args.update(discarded_keys) 1794 except Exception: 1795 # allOf integer schema will throw exception 1796 pass 1797 else: 1798 try: 1799 all_keys = set(model_to_dict(instance, serialize=False).keys()) 1800 js_keys = model_to_dict(instance, serialize=True).keys() 1801 all_keys.update(js_keys) 1802 discarded_keys = model_arg_keys - all_keys 1803 discarded_args.update(discarded_keys) 1804 except Exception: 1805 # allOf integer schema will throw exception 1806 pass 1807 return discarded_args 1808 1809 1810def validate_get_composed_info(constant_args, model_args, self): 1811 """ 1812 For composed schemas, generate schema instances for 1813 all schemas in the oneOf/anyOf/allOf definition. If additional 1814 properties are allowed, also assign those properties on 1815 all matched schemas that contain additionalProperties. 1816 Openapi schemas are python classes. 1817 1818 Exceptions are raised if: 1819 - 0 or > 1 oneOf schema matches the model_args input data 1820 - no anyOf schema matches the model_args input data 1821 - any of the allOf schemas do not match the model_args input data 1822 1823 Args: 1824 constant_args (dict): these are the args that every model requires 1825 model_args (dict): these are the required and optional spec args that 1826 were passed in to make this model 1827 self (class): the class that we are instantiating 1828 This class contains self._composed_schemas 1829 1830 Returns: 1831 composed_info (list): length three 1832 composed_instances (list): the composed instances which are not 1833 self 1834 var_name_to_model_instances (dict): a dict going from var_name 1835 to the model_instance which holds that var_name 1836 the model_instance may be self or an instance of one of the 1837 classes in self.composed_instances() 1838 additional_properties_model_instances (list): a list of the 1839 model instances which have the property 1840 additional_properties_type. This list can include self 1841 """ 1842 # create composed_instances 1843 composed_instances = [] 1844 allof_instances = get_allof_instances(self, model_args, constant_args) 1845 composed_instances.extend(allof_instances) 1846 oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) 1847 if oneof_instance is not None: 1848 composed_instances.append(oneof_instance) 1849 anyof_instances = get_anyof_instances(self, model_args, constant_args) 1850 composed_instances.extend(anyof_instances) 1851 """ 1852 set additional_properties_model_instances 1853 additional properties must be evaluated at the schema level 1854 so self's additional properties are most important 1855 If self is a composed schema with: 1856 - no properties defined in self 1857 - additionalProperties: False 1858 Then for object payloads every property is an additional property 1859 and they are not allowed, so only empty dict is allowed 1860 1861 Properties must be set on all matching schemas 1862 so when a property is assigned toa composed instance, it must be set on all 1863 composed instances regardless of additionalProperties presence 1864 keeping it to prevent breaking changes in v5.0.1 1865 TODO remove cls._additional_properties_model_instances in 6.0.0 1866 """ 1867 additional_properties_model_instances = [] 1868 if self.additional_properties_type is not None: 1869 additional_properties_model_instances = [self] 1870 1871 """ 1872 no need to set properties on self in here, they will be set in __init__ 1873 By here all composed schema oneOf/anyOf/allOf instances have their properties set using 1874 model_args 1875 """ 1876 discarded_args = get_discarded_args(self, composed_instances, model_args) 1877 1878 # map variable names to composed_instances 1879 var_name_to_model_instances = {} 1880 for prop_name in model_args: 1881 if prop_name not in discarded_args: 1882 var_name_to_model_instances[prop_name] = [self] + composed_instances 1883 1884 return [composed_instances, var_name_to_model_instances, additional_properties_model_instances, discarded_args]
34def convert_js_args_to_python_args(fn): 35 from functools import wraps 36 37 @wraps(fn) 38 def wrapped_init(_self, *args, **kwargs): 39 """ 40 An attribute named `self` received from the api will conflicts with the reserved `self` 41 parameter of a class method. During generation, `self` attributes are mapped 42 to `_self` in models. Here, we name `_self` instead of `self` to avoid conflicts. 43 """ 44 spec_property_naming = kwargs.get("_spec_property_naming", False) 45 if spec_property_naming: 46 kwargs = change_keys_js_to_python(kwargs, _self if isinstance(_self, type) else _self.__class__) 47 return fn(_self, *args, **kwargs) 48 49 return wrapped_init
52class cached_property(object): 53 # this caches the result of the function call for fn with no inputs 54 # use this as a decorator on function methods that you want converted 55 # into cached properties 56 result_key = "_results" 57 58 def __init__(self, fn): 59 self._fn = fn 60 61 def __get__(self, instance, cls=None): 62 if self.result_key in vars(self): 63 return vars(self)[self.result_key] 64 else: 65 result = self._fn() 66 setattr(self, self.result_key, result) 67 return result
73def allows_single_value_input(cls): 74 """ 75 This function returns True if the input composed schema model or any 76 descendant model allows a value only input 77 This is true for cases where oneOf contains items like: 78 oneOf: 79 - float 80 - NumberWithValidation 81 - StringEnum 82 - ArrayModel 83 - null 84 TODO: lru_cache this 85 """ 86 if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 87 return True 88 elif issubclass(cls, ModelComposed): 89 if not cls._composed_schemas["oneOf"]: 90 return False 91 return any(allows_single_value_input(c) for c in cls._composed_schemas["oneOf"]) 92 return False
This function returns True if the input composed schema model or any descendant model allows a value only input This is true for cases where oneOf contains items like: oneOf:
- float
- NumberWithValidation
- StringEnum
- ArrayModel
- null TODO: lru_cache this
95def composed_model_input_classes(cls): 96 """ 97 This function returns a list of the possible models that can be accepted as 98 inputs. 99 TODO: lru_cache this 100 """ 101 if issubclass(cls, ModelSimple) or cls in PRIMITIVE_TYPES: 102 return [cls] 103 elif issubclass(cls, ModelNormal): 104 if cls.discriminator is None: 105 return [cls] 106 else: 107 return get_discriminated_classes(cls) 108 elif issubclass(cls, ModelComposed): 109 if not cls._composed_schemas["oneOf"]: 110 return [] 111 if cls.discriminator is None: 112 input_classes = [] 113 for c in cls._composed_schemas["oneOf"]: 114 input_classes.extend(composed_model_input_classes(c)) 115 return input_classes 116 else: 117 return get_discriminated_classes(cls) 118 return []
This function returns a list of the possible models that can be accepted as inputs. TODO: lru_cache this
121class OpenApiModel(object): 122 """The base class for all OpenAPIModels""" 123 124 def set_attribute(self, name, value): 125 # this is only used to set properties on self 126 127 path_to_item = [] 128 if self._path_to_item: 129 path_to_item.extend(self._path_to_item) 130 path_to_item.append(name) 131 132 if name in self.openapi_types: 133 required_types_mixed = self.openapi_types[name] 134 elif self.additional_properties_type is None: 135 raise ApiAttributeError("{0} has no attribute '{1}'".format(type(self).__name__, name), path_to_item) 136 elif self.additional_properties_type is not None: 137 required_types_mixed = self.additional_properties_type 138 139 if get_simple_class(name) != str: 140 error_msg = type_error_message(var_name=name, var_value=name, valid_classes=(str,), key_type=True) 141 raise ApiTypeError(error_msg, path_to_item=path_to_item, valid_classes=(str,), key_type=True) 142 143 if self._check_type: 144 value = validate_and_convert_types( 145 value, 146 required_types_mixed, 147 path_to_item, 148 self._spec_property_naming, 149 self._check_type, 150 configuration=self._configuration, 151 ) 152 if (name,) in self.allowed_values: 153 check_allowed_values(self.allowed_values, (name,), value) 154 if (name,) in self.validations: 155 check_validations(self.validations, (name,), value, self._configuration) 156 self.__dict__["_data_store"][name] = value 157 158 def __repr__(self): 159 """For `print` and `pprint`""" 160 return self.to_str() 161 162 def __ne__(self, other): 163 """Returns true if both objects are not equal""" 164 return not self == other 165 166 def __setattr__(self, attr, value): 167 """set the value of an attribute using dot notation: `instance.attr = val`""" 168 self[attr] = value 169 170 def __getattr__(self, attr): 171 """get the value of an attribute using dot notation: `instance.attr`""" 172 return self.get(attr) 173 174 def __new__(cls, *args, **kwargs): 175 # this function uses the discriminator to 176 # pick a new schema/class to instantiate because a discriminator 177 # propertyName value was passed in 178 179 if len(args) == 1: 180 arg = args[0] 181 if arg is None and is_type_nullable(cls): 182 # The input data is the 'null' value and the type is nullable. 183 return None 184 185 if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 186 model_kwargs = {} 187 oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) 188 return oneof_instance 189 190 visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 191 if cls.discriminator is None or cls in visited_composed_classes: 192 # Use case 1: this openapi schema (cls) does not have a discriminator 193 # Use case 2: we have already visited this class before and are sure that we 194 # want to instantiate it this time. We have visited this class deserializing 195 # a payload with a discriminator. During that process we traveled through 196 # this class but did not make an instance of it. Now we are making an 197 # instance of a composed class which contains cls in it, so this time make an instance of cls. 198 # 199 # Here's an example of use case 2: If Animal has a discriminator 200 # petType and we pass in "Dog", and the class Dog 201 # allOf includes Animal, we move through Animal 202 # once using the discriminator, and pick Dog. 203 # Then in the composed schema dog Dog, we will make an instance of the 204 # Animal class (because Dal has allOf: Animal) but this time we won't travel 205 # through Animal's discriminator because we passed in 206 # _visited_composed_classes = (Animal,) 207 208 return super(OpenApiModel, cls).__new__(cls) 209 210 # Get the name and value of the discriminator property. 211 # The discriminator name is obtained from the discriminator meta-data 212 # and the discriminator value is obtained from the input data. 213 discr_propertyname_py = list(cls.discriminator.keys())[0] 214 discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 215 if discr_propertyname_js in kwargs: 216 discr_value = kwargs[discr_propertyname_js] 217 elif discr_propertyname_py in kwargs: 218 discr_value = kwargs[discr_propertyname_py] 219 else: 220 # The input data does not contain the discriminator property. 221 path_to_item = kwargs.get("_path_to_item", ()) 222 raise ApiValueError( 223 "Cannot deserialize input data due to missing discriminator. " 224 "The discriminator property '%s' is missing at path: %s" % (discr_propertyname_js, path_to_item) 225 ) 226 227 # Implementation note: the last argument to get_discriminator_class 228 # is a list of visited classes. get_discriminator_class may recursively 229 # call itself and update the list of visited classes, and the initial 230 # value must be an empty list. Hence not using 'visited_composed_classes' 231 new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 232 if new_cls is None: 233 path_to_item = kwargs.get("_path_to_item", ()) 234 disc_prop_value = kwargs.get(discr_propertyname_js, kwargs.get(discr_propertyname_py)) 235 raise ApiValueError( 236 "Cannot deserialize input data due to invalid discriminator " 237 "value. The OpenAPI document has no mapping for discriminator " 238 "property '%s'='%s' at path: %s" % (discr_propertyname_js, disc_prop_value, path_to_item) 239 ) 240 241 if new_cls in visited_composed_classes: 242 # if we are making an instance of a composed schema Descendent 243 # which allOf includes Ancestor, then Ancestor contains 244 # a discriminator that includes Descendent. 245 # So if we make an instance of Descendent, we have to make an 246 # instance of Ancestor to hold the allOf properties. 247 # This code detects that use case and makes the instance of Ancestor 248 # For example: 249 # When making an instance of Dog, _visited_composed_classes = (Dog,) 250 # then we make an instance of Animal to include in dog._composed_instances 251 # so when we are here, cls is Animal 252 # cls.discriminator != None 253 # cls not in _visited_composed_classes 254 # new_cls = Dog 255 # but we know we know that we already have Dog 256 # because it is in visited_composed_classes 257 # so make Animal here 258 return super(OpenApiModel, cls).__new__(cls) 259 260 # Build a list containing all oneOf and anyOf descendants. 261 oneof_anyof_classes = None 262 if cls._composed_schemas is not None: 263 oneof_anyof_classes = cls._composed_schemas.get("oneOf", ()) + cls._composed_schemas.get("anyOf", ()) 264 oneof_anyof_child = new_cls in oneof_anyof_classes 265 kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 266 267 if cls._composed_schemas.get("allOf") and oneof_anyof_child: 268 # Validate that we can make self because when we make the 269 # new_cls it will not include the allOf validations in self 270 self_inst = super(OpenApiModel, cls).__new__(cls) 271 self_inst.__init__(*args, **kwargs) 272 273 new_inst = new_cls.__new__(new_cls, *args, **kwargs) 274 new_inst.__init__(*args, **kwargs) 275 return new_inst 276 277 @classmethod 278 @convert_js_args_to_python_args 279 def _new_from_openapi_data(cls, *args, **kwargs): 280 # this function uses the discriminator to 281 # pick a new schema/class to instantiate because a discriminator 282 # propertyName value was passed in 283 284 if len(args) == 1: 285 arg = args[0] 286 if arg is None and is_type_nullable(cls): 287 # The input data is the 'null' value and the type is nullable. 288 return None 289 290 if issubclass(cls, ModelComposed) and allows_single_value_input(cls): 291 model_kwargs = {} 292 oneof_instance = get_oneof_instance(cls, model_kwargs, kwargs, model_arg=arg) 293 return oneof_instance 294 295 visited_composed_classes = kwargs.get("_visited_composed_classes", ()) 296 if cls.discriminator is None or cls in visited_composed_classes: 297 # Use case 1: this openapi schema (cls) does not have a discriminator 298 # Use case 2: we have already visited this class before and are sure that we 299 # want to instantiate it this time. We have visited this class deserializing 300 # a payload with a discriminator. During that process we traveled through 301 # this class but did not make an instance of it. Now we are making an 302 # instance of a composed class which contains cls in it, so this time make an instance of cls. 303 # 304 # Here's an example of use case 2: If Animal has a discriminator 305 # petType and we pass in "Dog", and the class Dog 306 # allOf includes Animal, we move through Animal 307 # once using the discriminator, and pick Dog. 308 # Then in the composed schema dog Dog, we will make an instance of the 309 # Animal class (because Dal has allOf: Animal) but this time we won't travel 310 # through Animal's discriminator because we passed in 311 # _visited_composed_classes = (Animal,) 312 313 return cls._from_openapi_data(*args, **kwargs) 314 315 # Get the name and value of the discriminator property. 316 # The discriminator name is obtained from the discriminator meta-data 317 # and the discriminator value is obtained from the input data. 318 discr_propertyname_py = list(cls.discriminator.keys())[0] 319 discr_propertyname_js = cls.attribute_map[discr_propertyname_py] 320 if discr_propertyname_js in kwargs: 321 discr_value = kwargs[discr_propertyname_js] 322 elif discr_propertyname_py in kwargs: 323 discr_value = kwargs[discr_propertyname_py] 324 else: 325 # The input data does not contain the discriminator property. 326 path_to_item = kwargs.get("_path_to_item", ()) 327 raise ApiValueError( 328 "Cannot deserialize input data due to missing discriminator. " 329 "The discriminator property '%s' is missing at path: %s" % (discr_propertyname_js, path_to_item) 330 ) 331 332 # Implementation note: the last argument to get_discriminator_class 333 # is a list of visited classes. get_discriminator_class may recursively 334 # call itself and update the list of visited classes, and the initial 335 # value must be an empty list. Hence not using 'visited_composed_classes' 336 new_cls = get_discriminator_class(cls, discr_propertyname_py, discr_value, []) 337 if new_cls is None: 338 path_to_item = kwargs.get("_path_to_item", ()) 339 disc_prop_value = kwargs.get(discr_propertyname_js, kwargs.get(discr_propertyname_py)) 340 raise ApiValueError( 341 "Cannot deserialize input data due to invalid discriminator " 342 "value. The OpenAPI document has no mapping for discriminator " 343 "property '%s'='%s' at path: %s" % (discr_propertyname_js, disc_prop_value, path_to_item) 344 ) 345 346 if new_cls in visited_composed_classes: 347 # if we are making an instance of a composed schema Descendent 348 # which allOf includes Ancestor, then Ancestor contains 349 # a discriminator that includes Descendent. 350 # So if we make an instance of Descendent, we have to make an 351 # instance of Ancestor to hold the allOf properties. 352 # This code detects that use case and makes the instance of Ancestor 353 # For example: 354 # When making an instance of Dog, _visited_composed_classes = (Dog,) 355 # then we make an instance of Animal to include in dog._composed_instances 356 # so when we are here, cls is Animal 357 # cls.discriminator != None 358 # cls not in _visited_composed_classes 359 # new_cls = Dog 360 # but we know we know that we already have Dog 361 # because it is in visited_composed_classes 362 # so make Animal here 363 return cls._from_openapi_data(*args, **kwargs) 364 365 # Build a list containing all oneOf and anyOf descendants. 366 oneof_anyof_classes = None 367 if cls._composed_schemas is not None: 368 oneof_anyof_classes = cls._composed_schemas.get("oneOf", ()) + cls._composed_schemas.get("anyOf", ()) 369 oneof_anyof_child = new_cls in oneof_anyof_classes 370 kwargs["_visited_composed_classes"] = visited_composed_classes + (cls,) 371 372 if cls._composed_schemas.get("allOf") and oneof_anyof_child: 373 # Validate that we can make self because when we make the 374 # new_cls it will not include the allOf validations in self 375 self_inst = cls._from_openapi_data(*args, **kwargs) 376 377 new_inst = new_cls._new_from_openapi_data(*args, **kwargs) 378 return new_inst
The base class for all OpenAPIModels
124 def set_attribute(self, name, value): 125 # this is only used to set properties on self 126 127 path_to_item = [] 128 if self._path_to_item: 129 path_to_item.extend(self._path_to_item) 130 path_to_item.append(name) 131 132 if name in self.openapi_types: 133 required_types_mixed = self.openapi_types[name] 134 elif self.additional_properties_type is None: 135 raise ApiAttributeError("{0} has no attribute '{1}'".format(type(self).__name__, name), path_to_item) 136 elif self.additional_properties_type is not None: 137 required_types_mixed = self.additional_properties_type 138 139 if get_simple_class(name) != str: 140 error_msg = type_error_message(var_name=name, var_value=name, valid_classes=(str,), key_type=True) 141 raise ApiTypeError(error_msg, path_to_item=path_to_item, valid_classes=(str,), key_type=True) 142 143 if self._check_type: 144 value = validate_and_convert_types( 145 value, 146 required_types_mixed, 147 path_to_item, 148 self._spec_property_naming, 149 self._check_type, 150 configuration=self._configuration, 151 ) 152 if (name,) in self.allowed_values: 153 check_allowed_values(self.allowed_values, (name,), value) 154 if (name,) in self.validations: 155 check_validations(self.validations, (name,), value, self._configuration) 156 self.__dict__["_data_store"][name] = value
381class ModelSimple(OpenApiModel): 382 """the parent class of models whose type != object in their 383 swagger/openapi""" 384 385 def __setitem__(self, name, value): 386 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 387 if name in self.required_properties: 388 self.__dict__[name] = value 389 return 390 391 self.set_attribute(name, value) 392 393 def get(self, name, default=None): 394 """returns the value of an attribute or some default value if the attribute was not set""" 395 if name in self.required_properties: 396 return self.__dict__[name] 397 398 return self.__dict__["_data_store"].get(name, default) 399 400 def __getitem__(self, name): 401 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 402 if name in self: 403 return self.get(name) 404 405 raise ApiAttributeError( 406 "{0} has no attribute '{1}'".format(type(self).__name__, name), [e for e in [self._path_to_item, name] if e] 407 ) 408 409 def __contains__(self, name): 410 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 411 if name in self.required_properties: 412 return name in self.__dict__ 413 414 return name in self.__dict__["_data_store"] 415 416 def to_str(self): 417 """Returns the string representation of the model""" 418 return str(self.value) 419 420 def __eq__(self, other): 421 """Returns true if both objects are equal""" 422 if not isinstance(other, self.__class__): 423 return False 424 425 this_val = self._data_store["value"] 426 that_val = other._data_store["value"] 427 types = set() 428 types.add(this_val.__class__) 429 types.add(that_val.__class__) 430 vals_equal = this_val == that_val 431 return vals_equal
the parent class of models whose type != object in their swagger/openapi
393 def get(self, name, default=None): 394 """returns the value of an attribute or some default value if the attribute was not set""" 395 if name in self.required_properties: 396 return self.__dict__[name] 397 398 return self.__dict__["_data_store"].get(name, default)
returns the value of an attribute or some default value if the attribute was not set
416 def to_str(self): 417 """Returns the string representation of the model""" 418 return str(self.value)
Returns the string representation of the model
Inherited Members
434class ModelNormal(OpenApiModel): 435 """the parent class of models whose type == object in their 436 swagger/openapi""" 437 438 def __setitem__(self, name, value): 439 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 440 if name in self.required_properties: 441 self.__dict__[name] = value 442 return 443 444 self.set_attribute(name, value) 445 446 def get(self, name, default=None): 447 """returns the value of an attribute or some default value if the attribute was not set""" 448 if name in self.required_properties: 449 return self.__dict__[name] 450 451 return self.__dict__["_data_store"].get(name, default) 452 453 def __getitem__(self, name): 454 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 455 if name in self: 456 return self.get(name) 457 458 raise ApiAttributeError( 459 "{0} has no attribute '{1}'".format(type(self).__name__, name), [e for e in [self._path_to_item, name] if e] 460 ) 461 462 def __contains__(self, name): 463 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 464 if name in self.required_properties: 465 return name in self.__dict__ 466 467 return name in self.__dict__["_data_store"] 468 469 def to_dict(self): 470 """Returns the model properties as a dict""" 471 return model_to_dict(self, serialize=False) 472 473 def to_str(self): 474 """Returns the string representation of the model""" 475 return pprint.pformat(self.to_dict()) 476 477 def __eq__(self, other): 478 """Returns true if both objects are equal""" 479 if not isinstance(other, self.__class__): 480 return False 481 482 if not set(self._data_store.keys()) == set(other._data_store.keys()): 483 return False 484 for _var_name, this_val in self._data_store.items(): 485 that_val = other._data_store[_var_name] 486 types = set() 487 types.add(this_val.__class__) 488 types.add(that_val.__class__) 489 vals_equal = this_val == that_val 490 if not vals_equal: 491 return False 492 return True
the parent class of models whose type == object in their swagger/openapi
446 def get(self, name, default=None): 447 """returns the value of an attribute or some default value if the attribute was not set""" 448 if name in self.required_properties: 449 return self.__dict__[name] 450 451 return self.__dict__["_data_store"].get(name, default)
returns the value of an attribute or some default value if the attribute was not set
469 def to_dict(self): 470 """Returns the model properties as a dict""" 471 return model_to_dict(self, serialize=False)
Returns the model properties as a dict
473 def to_str(self): 474 """Returns the string representation of the model""" 475 return pprint.pformat(self.to_dict())
Returns the string representation of the model
Inherited Members
495class ModelComposed(OpenApiModel): 496 """the parent class of models whose type == object in their 497 swagger/openapi and have oneOf/allOf/anyOf 498 499 When one sets a property we use var_name_to_model_instances to store the value in 500 the correct class instances + run any type checking + validation code. 501 When one gets a property we use var_name_to_model_instances to get the value 502 from the correct class instances. 503 This allows multiple composed schemas to contain the same property with additive 504 constraints on the value. 505 506 _composed_schemas (dict) stores the anyOf/allOf/oneOf classes 507 key (str): allOf/oneOf/anyOf 508 value (list): the classes in the XOf definition. 509 Note: none_type can be included when the openapi document version >= 3.1.0 510 _composed_instances (list): stores a list of instances of the composed schemas 511 defined in _composed_schemas. When properties are accessed in the self instance, 512 they are returned from the self._data_store or the data stores in the instances 513 in self._composed_schemas 514 _var_name_to_model_instances (dict): maps between a variable name on self and 515 the composed instances (self included) which contain that data 516 key (str): property name 517 value (list): list of class instances, self or instances in _composed_instances 518 which contain the value that the key is referring to. 519 """ 520 521 def __setitem__(self, name, value): 522 """set the value of an attribute using square-bracket notation: `instance[attr] = val`""" 523 if name in self.required_properties: 524 self.__dict__[name] = value 525 return 526 527 """ 528 Use cases: 529 1. additional_properties_type is None (additionalProperties == False in spec) 530 Check for property presence in self.openapi_types 531 if not present then throw an error 532 if present set in self, set attribute 533 always set on composed schemas 534 2. additional_properties_type exists 535 set attribute on self 536 always set on composed schemas 537 """ 538 if self.additional_properties_type is None: 539 """ 540 For an attribute to exist on a composed schema it must: 541 - fulfill schema_requirements in the self composed schema not considering oneOf/anyOf/allOf schemas AND 542 - fulfill schema_requirements in each oneOf/anyOf/allOf schemas 543 544 schema_requirements: 545 For an attribute to exist on a schema it must: 546 - be present in properties at the schema OR 547 - have additionalProperties unset (defaults additionalProperties = any type) OR 548 - have additionalProperties set 549 """ 550 if name not in self.openapi_types: 551 raise ApiAttributeError( 552 "{0} has no attribute '{1}'".format(type(self).__name__, name), 553 [e for e in [self._path_to_item, name] if e], 554 ) 555 # attribute must be set on self and composed instances 556 self.set_attribute(name, value) 557 for model_instance in self._composed_instances: 558 setattr(model_instance, name, value) 559 if name not in self._var_name_to_model_instances: 560 # we assigned an additional property 561 self.__dict__["_var_name_to_model_instances"][name] = self._composed_instances + [self] 562 return None 563 564 __unset_attribute_value__ = object() 565 566 def get(self, name, default=None): 567 """returns the value of an attribute or some default value if the attribute was not set""" 568 if name in self.required_properties: 569 return self.__dict__[name] 570 571 # get the attribute from the correct instance 572 model_instances = self._var_name_to_model_instances.get(name) 573 values = [] 574 # A composed model stores self and child (oneof/anyOf/allOf) models under 575 # self._var_name_to_model_instances. 576 # Any property must exist in self and all model instances 577 # The value stored in all model instances must be the same 578 if model_instances: 579 for model_instance in model_instances: 580 if name in model_instance._data_store: 581 v = model_instance._data_store[name] 582 if v not in values: 583 values.append(v) 584 len_values = len(values) 585 if len_values == 0: 586 return default 587 elif len_values == 1: 588 return values[0] 589 elif len_values > 1: 590 raise ApiValueError( 591 "Values stored for property {0} in {1} differ when looking " 592 "at self and self's composed instances. All values must be " 593 "the same".format(name, type(self).__name__), 594 [e for e in [self._path_to_item, name] if e], 595 ) 596 597 def __getitem__(self, name): 598 """get the value of an attribute using square-bracket notation: `instance[attr]`""" 599 value = self.get(name, self.__unset_attribute_value__) 600 if value is self.__unset_attribute_value__: 601 raise ApiAttributeError( 602 "{0} has no attribute '{1}'".format(type(self).__name__, name), 603 [e for e in [self._path_to_item, name] if e], 604 ) 605 return value 606 607 def __contains__(self, name): 608 """used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`""" 609 610 if name in self.required_properties: 611 return name in self.__dict__ 612 613 model_instances = self._var_name_to_model_instances.get(name, self._additional_properties_model_instances) 614 615 if model_instances: 616 for model_instance in model_instances: 617 if name in model_instance._data_store: 618 return True 619 620 return False 621 622 def to_dict(self): 623 """Returns the model properties as a dict""" 624 return model_to_dict(self, serialize=False) 625 626 def to_str(self): 627 """Returns the string representation of the model""" 628 return pprint.pformat(self.to_dict()) 629 630 def __eq__(self, other): 631 """Returns true if both objects are equal""" 632 if not isinstance(other, self.__class__): 633 return False 634 635 if not set(self._data_store.keys()) == set(other._data_store.keys()): 636 return False 637 for _var_name, this_val in self._data_store.items(): 638 that_val = other._data_store[_var_name] 639 types = set() 640 types.add(this_val.__class__) 641 types.add(that_val.__class__) 642 vals_equal = this_val == that_val 643 if not vals_equal: 644 return False 645 return True
the parent class of models whose type == object in their swagger/openapi and have oneOf/allOf/anyOf
When one sets a property we use var_name_to_model_instances to store the value in the correct class instances + run any type checking + validation code. When one gets a property we use var_name_to_model_instances to get the value from the correct class instances. This allows multiple composed schemas to contain the same property with additive constraints on the value.
_composed_schemas (dict) stores the anyOf/allOf/oneOf classes key (str): allOf/oneOf/anyOf value (list): the classes in the XOf definition. Note: none_type can be included when the openapi document version >= 3.1.0 _composed_instances (list): stores a list of instances of the composed schemas defined in _composed_schemas. When properties are accessed in the self instance, they are returned from the self._data_store or the data stores in the instances in self._composed_schemas _var_name_to_model_instances (dict): maps between a variable name on self and the composed instances (self included) which contain that data key (str): property name value (list): list of class instances, self or instances in _composed_instances which contain the value that the key is referring to.
566 def get(self, name, default=None): 567 """returns the value of an attribute or some default value if the attribute was not set""" 568 if name in self.required_properties: 569 return self.__dict__[name] 570 571 # get the attribute from the correct instance 572 model_instances = self._var_name_to_model_instances.get(name) 573 values = [] 574 # A composed model stores self and child (oneof/anyOf/allOf) models under 575 # self._var_name_to_model_instances. 576 # Any property must exist in self and all model instances 577 # The value stored in all model instances must be the same 578 if model_instances: 579 for model_instance in model_instances: 580 if name in model_instance._data_store: 581 v = model_instance._data_store[name] 582 if v not in values: 583 values.append(v) 584 len_values = len(values) 585 if len_values == 0: 586 return default 587 elif len_values == 1: 588 return values[0] 589 elif len_values > 1: 590 raise ApiValueError( 591 "Values stored for property {0} in {1} differ when looking " 592 "at self and self's composed instances. All values must be " 593 "the same".format(name, type(self).__name__), 594 [e for e in [self._path_to_item, name] if e], 595 )
returns the value of an attribute or some default value if the attribute was not set
622 def to_dict(self): 623 """Returns the model properties as a dict""" 624 return model_to_dict(self, serialize=False)
Returns the model properties as a dict
626 def to_str(self): 627 """Returns the string representation of the model""" 628 return pprint.pformat(self.to_dict())
Returns the string representation of the model
Inherited Members
722def get_simple_class(input_value): 723 """Returns an input_value's simple class that we will use for type checking 724 Python2: 725 float and int will return int, where int is the python3 int backport 726 str and unicode will return str, where str is the python3 str backport 727 Note: float and int ARE both instances of int backport 728 Note: str_py2 and unicode_py2 are NOT both instances of str backport 729 730 Args: 731 input_value (class/class_instance): the item for which we will return 732 the simple class 733 """ 734 if isinstance(input_value, type): 735 # input_value is a class 736 return input_value 737 elif isinstance(input_value, tuple): 738 return tuple 739 elif isinstance(input_value, list): 740 return list 741 elif isinstance(input_value, dict): 742 return dict 743 elif isinstance(input_value, none_type): 744 return none_type 745 elif isinstance(input_value, file_type): 746 return file_type 747 elif isinstance(input_value, bool): 748 # this must be higher than the int check because 749 # isinstance(True, int) == True 750 return bool 751 elif isinstance(input_value, int): 752 return int 753 elif isinstance(input_value, datetime): 754 # this must be higher than the date check because 755 # isinstance(datetime_instance, date) == True 756 return datetime 757 elif isinstance(input_value, date): 758 return date 759 elif isinstance(input_value, str): 760 return str 761 return type(input_value)
Returns an input_value's simple class that we will use for type checking Python2: float and int will return int, where int is the python3 int backport str and unicode will return str, where str is the python3 str backport Note: float and int ARE both instances of int backport Note: str_py2 and unicode_py2 are NOT both instances of str backport
Arguments:
- input_value (class/class_instance): the item for which we will return the simple class
764def check_allowed_values(allowed_values, input_variable_path, input_values): 765 """Raises an exception if the input_values are not allowed 766 767 Args: 768 allowed_values (dict): the allowed_values dict 769 input_variable_path (tuple): the path to the input variable 770 input_values (list/str/int/float/date/datetime): the values that we 771 are checking to see if they are in allowed_values 772 """ 773 these_allowed_values = list(allowed_values[input_variable_path].values()) 774 if isinstance(input_values, list) and not set(input_values).issubset(set(these_allowed_values)): 775 invalid_values = (", ".join(map(str, set(input_values) - set(these_allowed_values))),) 776 raise ApiValueError( 777 "Invalid values for `%s` [%s], must be a subset of [%s]" 778 % (input_variable_path[0], invalid_values, ", ".join(map(str, these_allowed_values))) 779 ) 780 elif isinstance(input_values, dict) and not set(input_values.keys()).issubset(set(these_allowed_values)): 781 invalid_values = ", ".join(map(str, set(input_values.keys()) - set(these_allowed_values))) 782 raise ApiValueError( 783 "Invalid keys in `%s` [%s], must be a subset of [%s]" 784 % (input_variable_path[0], invalid_values, ", ".join(map(str, these_allowed_values))) 785 ) 786 elif not isinstance(input_values, (list, dict)) and input_values not in these_allowed_values: 787 raise ApiValueError( 788 "Invalid value for `%s` (%s), must be one of %s" 789 % (input_variable_path[0], input_values, these_allowed_values) 790 )
Raises an exception if the input_values are not allowed
Arguments:
- allowed_values (dict): the allowed_values dict
- input_variable_path (tuple): the path to the input variable
- input_values (list/str/int/float/date/datetime): the values that we are checking to see if they are in allowed_values
793def is_json_validation_enabled(schema_keyword, configuration=None): 794 """Returns true if JSON schema validation is enabled for the specified 795 validation keyword. This can be used to skip JSON schema structural validation 796 as requested in the configuration. 797 798 Args: 799 schema_keyword (string): the name of a JSON schema validation keyword. 800 configuration (Configuration): the configuration class. 801 """ 802 803 return ( 804 configuration is None 805 or not hasattr(configuration, "_disabled_client_side_validations") 806 or schema_keyword not in configuration._disabled_client_side_validations 807 )
Returns true if JSON schema validation is enabled for the specified validation keyword. This can be used to skip JSON schema structural validation as requested in the configuration.
Arguments:
- schema_keyword (string): the name of a JSON schema validation keyword.
- configuration (Configuration): the configuration class.
810def check_validations(validations, input_variable_path, input_values, configuration=None): 811 """Raises an exception if the input_values are invalid 812 813 Args: 814 validations (dict): the validation dictionary. 815 input_variable_path (tuple): the path to the input variable. 816 input_values (list/str/int/float/date/datetime): the values that we 817 are checking. 818 configuration (Configuration): the configuration class. 819 """ 820 821 if input_values is None: 822 return 823 824 current_validations = validations[input_variable_path] 825 if ( 826 is_json_validation_enabled("multipleOf", configuration) 827 and "multiple_of" in current_validations 828 and isinstance(input_values, (int, float)) 829 and not (float(input_values) / current_validations["multiple_of"]).is_integer() 830 ): 831 # Note 'multipleOf' will be as good as the floating point arithmetic. 832 raise ApiValueError( 833 "Invalid value for `%s`, value must be a multiple of " 834 "`%s`" % (input_variable_path[0], current_validations["multiple_of"]) 835 ) 836 837 if ( 838 is_json_validation_enabled("maxLength", configuration) 839 and "max_length" in current_validations 840 and len(input_values) > current_validations["max_length"] 841 ): 842 raise ApiValueError( 843 "Invalid value for `%s`, length must be less than or equal to " 844 "`%s`" % (input_variable_path[0], current_validations["max_length"]) 845 ) 846 847 if ( 848 is_json_validation_enabled("minLength", configuration) 849 and "min_length" in current_validations 850 and len(input_values) < current_validations["min_length"] 851 ): 852 raise ApiValueError( 853 "Invalid value for `%s`, length must be greater than or equal to " 854 "`%s`" % (input_variable_path[0], current_validations["min_length"]) 855 ) 856 857 if ( 858 is_json_validation_enabled("maxItems", configuration) 859 and "max_items" in current_validations 860 and len(input_values) > current_validations["max_items"] 861 ): 862 raise ApiValueError( 863 "Invalid value for `%s`, number of items must be less than or " 864 "equal to `%s`" % (input_variable_path[0], current_validations["max_items"]) 865 ) 866 867 if ( 868 is_json_validation_enabled("minItems", configuration) 869 and "min_items" in current_validations 870 and len(input_values) < current_validations["min_items"] 871 ): 872 raise ValueError( 873 "Invalid value for `%s`, number of items must be greater than or " 874 "equal to `%s`" % (input_variable_path[0], current_validations["min_items"]) 875 ) 876 877 items = ("exclusive_maximum", "inclusive_maximum", "exclusive_minimum", "inclusive_minimum") 878 if any(item in current_validations for item in items): 879 if isinstance(input_values, list): 880 max_val = max(input_values) 881 min_val = min(input_values) 882 elif isinstance(input_values, dict): 883 max_val = max(input_values.values()) 884 min_val = min(input_values.values()) 885 else: 886 max_val = input_values 887 min_val = input_values 888 889 if ( 890 is_json_validation_enabled("exclusiveMaximum", configuration) 891 and "exclusive_maximum" in current_validations 892 and max_val >= current_validations["exclusive_maximum"] 893 ): 894 raise ApiValueError( 895 "Invalid value for `%s`, must be a value less than `%s`" 896 % (input_variable_path[0], current_validations["exclusive_maximum"]) 897 ) 898 899 if ( 900 is_json_validation_enabled("maximum", configuration) 901 and "inclusive_maximum" in current_validations 902 and max_val > current_validations["inclusive_maximum"] 903 ): 904 raise ApiValueError( 905 "Invalid value for `%s`, must be a value less than or equal to " 906 "`%s`" % (input_variable_path[0], current_validations["inclusive_maximum"]) 907 ) 908 909 if ( 910 is_json_validation_enabled("exclusiveMinimum", configuration) 911 and "exclusive_minimum" in current_validations 912 and min_val <= current_validations["exclusive_minimum"] 913 ): 914 raise ApiValueError( 915 "Invalid value for `%s`, must be a value greater than `%s`" 916 % (input_variable_path[0], current_validations["exclusive_maximum"]) 917 ) 918 919 if ( 920 is_json_validation_enabled("minimum", configuration) 921 and "inclusive_minimum" in current_validations 922 and min_val < current_validations["inclusive_minimum"] 923 ): 924 raise ApiValueError( 925 "Invalid value for `%s`, must be a value greater than or equal " 926 "to `%s`" % (input_variable_path[0], current_validations["inclusive_minimum"]) 927 ) 928 flags = current_validations.get("regex", {}).get("flags", 0) 929 if ( 930 is_json_validation_enabled("pattern", configuration) 931 and "regex" in current_validations 932 and not re.search(current_validations["regex"]["pattern"], input_values, flags=flags) 933 ): 934 err_msg = r"Invalid value for `%s`, must match regular expression `%s`" % ( 935 input_variable_path[0], 936 current_validations["regex"]["pattern"], 937 ) 938 if flags != 0: 939 # Don't print the regex flags if the flags are not 940 # specified in the OAS document. 941 err_msg = r"%s with flags=`%s`" % (err_msg, flags) 942 raise ApiValueError(err_msg)
Raises an exception if the input_values are invalid
Arguments:
- validations (dict): the validation dictionary.
- input_variable_path (tuple): the path to the input variable.
- input_values (list/str/int/float/date/datetime): the values that we are checking.
- configuration (Configuration): the configuration class.
945def order_response_types(required_types): 946 """Returns the required types sorted in coercion order 947 948 Args: 949 required_types (list/tuple): collection of classes or instance of 950 list or dict with class information inside it. 951 952 Returns: 953 (list): coercion order sorted collection of classes or instance 954 of list or dict with class information inside it. 955 """ 956 957 def index_getter(class_or_instance): 958 if isinstance(class_or_instance, list): 959 return COERCION_INDEX_BY_TYPE[list] 960 elif isinstance(class_or_instance, dict): 961 return COERCION_INDEX_BY_TYPE[dict] 962 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelComposed): 963 return COERCION_INDEX_BY_TYPE[ModelComposed] 964 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelNormal): 965 return COERCION_INDEX_BY_TYPE[ModelNormal] 966 elif inspect.isclass(class_or_instance) and issubclass(class_or_instance, ModelSimple): 967 return COERCION_INDEX_BY_TYPE[ModelSimple] 968 elif class_or_instance in COERCION_INDEX_BY_TYPE: 969 return COERCION_INDEX_BY_TYPE[class_or_instance] 970 raise ApiValueError("Unsupported type: %s" % class_or_instance) 971 972 sorted_types = sorted(required_types, key=lambda class_or_instance: index_getter(class_or_instance)) 973 return sorted_types
Returns the required types sorted in coercion order
Arguments:
- required_types (list/tuple): collection of classes or instance of list or dict with class information inside it.
Returns:
(list): coercion order sorted collection of classes or instance of list or dict with class information inside it.
976def remove_uncoercible(required_types_classes, current_item, spec_property_naming, must_convert=True): 977 """Only keeps the type conversions that are possible 978 979 Args: 980 required_types_classes (tuple): tuple of classes that are required 981 these should be ordered by COERCION_INDEX_BY_TYPE 982 spec_property_naming (bool): True if the variable names in the input 983 data are serialized names as specified in the OpenAPI document. 984 False if the variables names in the input data are python 985 variable names in PEP-8 snake case. 986 current_item (any): the current item (input data) to be converted 987 988 Keyword Args: 989 must_convert (bool): if True the item to convert is of the wrong 990 type and we want a big list of coercibles 991 if False, we want a limited list of coercibles 992 993 Returns: 994 (list): the remaining coercible required types, classes only 995 """ 996 current_type_simple = get_simple_class(current_item) 997 998 results_classes = [] 999 for required_type_class in required_types_classes: 1000 # convert our models to OpenApiModel 1001 required_type_class_simplified = required_type_class 1002 if isinstance(required_type_class_simplified, type): 1003 if issubclass(required_type_class_simplified, ModelComposed): 1004 required_type_class_simplified = ModelComposed 1005 elif issubclass(required_type_class_simplified, ModelNormal): 1006 required_type_class_simplified = ModelNormal 1007 elif issubclass(required_type_class_simplified, ModelSimple): 1008 required_type_class_simplified = ModelSimple 1009 1010 if required_type_class_simplified == current_type_simple: 1011 # don't consider converting to one's own class 1012 continue 1013 1014 class_pair = (current_type_simple, required_type_class_simplified) 1015 if must_convert and class_pair in COERCIBLE_TYPE_PAIRS[spec_property_naming]: 1016 results_classes.append(required_type_class) 1017 elif class_pair in UPCONVERSION_TYPE_PAIRS: 1018 results_classes.append(required_type_class) 1019 return results_classes
Only keeps the type conversions that are possible
Arguments:
- required_types_classes (tuple): tuple of classes that are required these should be ordered by COERCION_INDEX_BY_TYPE
- spec_property_naming (bool): True if the variable names in the input data are serialized names as specified in the OpenAPI document. False if the variables names in the input data are python variable names in PEP-8 snake case.
- current_item (any): the current item (input data) to be converted
Keyword Args:
must_convert (bool): if True the item to convert is of the wrong type and we want a big list of coercibles if False, we want a limited list of coercibles
Returns:
(list): the remaining coercible required types, classes only
1022def get_discriminated_classes(cls): 1023 """ 1024 Returns all the classes that a discriminator converts to 1025 TODO: lru_cache this 1026 """ 1027 possible_classes = [] 1028 key = list(cls.discriminator.keys())[0] 1029 if is_type_nullable(cls): 1030 possible_classes.append(cls) 1031 for discr_cls in cls.discriminator[key].values(): 1032 if hasattr(discr_cls, "discriminator") and discr_cls.discriminator is not None: 1033 possible_classes.extend(get_discriminated_classes(discr_cls)) 1034 else: 1035 possible_classes.append(discr_cls) 1036 return possible_classes
Returns all the classes that a discriminator converts to TODO: lru_cache this
1039def get_possible_classes(cls, from_server_context): 1040 # TODO: lru_cache this 1041 possible_classes = [cls] 1042 if from_server_context: 1043 return possible_classes 1044 if hasattr(cls, "discriminator") and cls.discriminator is not None: 1045 possible_classes = [] 1046 possible_classes.extend(get_discriminated_classes(cls)) 1047 elif issubclass(cls, ModelComposed): 1048 possible_classes.extend(composed_model_input_classes(cls)) 1049 return possible_classes
1052def get_required_type_classes(required_types_mixed, spec_property_naming): 1053 """Converts the tuple required_types into a tuple and a dict described 1054 below 1055 1056 Args: 1057 required_types_mixed (tuple/list): will contain either classes or 1058 instance of list or dict 1059 spec_property_naming (bool): if True these values came from the 1060 server, and we use the data types in our endpoints. 1061 If False, we are client side and we need to include 1062 oneOf and discriminator classes inside the data types in our endpoints 1063 1064 Returns: 1065 (valid_classes, dict_valid_class_to_child_types_mixed): 1066 valid_classes (tuple): the valid classes that the current item 1067 should be 1068 dict_valid_class_to_child_types_mixed (dict): 1069 valid_class (class): this is the key 1070 child_types_mixed (list/dict/tuple): describes the valid child 1071 types 1072 """ 1073 valid_classes = [] 1074 child_req_types_by_current_type = {} 1075 for required_type in required_types_mixed: 1076 if isinstance(required_type, list): 1077 valid_classes.append(list) 1078 child_req_types_by_current_type[list] = required_type 1079 elif isinstance(required_type, tuple): 1080 valid_classes.append(tuple) 1081 child_req_types_by_current_type[tuple] = required_type 1082 elif isinstance(required_type, dict): 1083 valid_classes.append(dict) 1084 child_req_types_by_current_type[dict] = required_type[str] 1085 else: 1086 valid_classes.extend(get_possible_classes(required_type, spec_property_naming)) 1087 return tuple(valid_classes), child_req_types_by_current_type
Converts the tuple required_types into a tuple and a dict described below
Arguments:
- required_types_mixed (tuple/list): will contain either classes or instance of list or dict
- spec_property_naming (bool): if True these values came from the server, and we use the data types in our endpoints. If False, we are client side and we need to include oneOf and discriminator classes inside the data types in our endpoints
Returns:
(valid_classes, dict_valid_class_to_child_types_mixed): valid_classes (tuple): the valid classes that the current item should be dict_valid_class_to_child_types_mixed (dict): valid_class (class): this is the key child_types_mixed (list/dict/tuple): describes the valid child types
1090def change_keys_js_to_python(input_dict, model_class): 1091 """ 1092 Converts from javascript_key keys in the input_dict to python_keys in 1093 the output dict using the mapping in model_class. 1094 If the input_dict contains a key which does not declared in the model_class, 1095 the key is added to the output dict as is. The assumption is the model_class 1096 may have undeclared properties (additionalProperties attribute in the OAS 1097 document). 1098 """ 1099 1100 if getattr(model_class, "attribute_map", None) is None: 1101 return input_dict 1102 output_dict = {} 1103 reversed_attr_map = {value: key for key, value in model_class.attribute_map.items()} 1104 for javascript_key, value in input_dict.items(): 1105 python_key = reversed_attr_map.get(javascript_key) 1106 if python_key is None: 1107 # if the key is unknown, it is in error or it is an 1108 # additionalProperties variable 1109 python_key = javascript_key 1110 output_dict[python_key] = value 1111 return output_dict
Converts from javascript_key keys in the input_dict to python_keys in the output dict using the mapping in model_class. If the input_dict contains a key which does not declared in the model_class, the key is added to the output dict as is. The assumption is the model_class may have undeclared properties (additionalProperties attribute in the OAS document).
1114def get_type_error(var_value, path_to_item, valid_classes, key_type=False): 1115 error_msg = type_error_message( 1116 var_name=path_to_item[-1], var_value=var_value, valid_classes=valid_classes, key_type=key_type 1117 ) 1118 return ApiTypeError(error_msg, path_to_item=path_to_item, valid_classes=valid_classes, key_type=key_type)
1121def deserialize_primitive(data, klass, path_to_item): 1122 """Deserializes string to primitive type. 1123 1124 :param data: str/int/float 1125 :param klass: str/class the class to convert to 1126 1127 :return: int, float, str, bool, date, datetime 1128 """ 1129 additional_message = "" 1130 try: 1131 if klass in {datetime, date}: 1132 additional_message = ( 1133 "If you need your parameter to have a fallback " 1134 "string value, please set its type as `type: {}` in your " 1135 "spec. That allows the value to be any type. " 1136 ) 1137 if klass == datetime: 1138 if len(data) < 8: 1139 raise ValueError("This is not a datetime") 1140 # The string should be in iso8601 datetime format. 1141 parsed_datetime = parse(data) 1142 date_only = ( 1143 parsed_datetime.hour == 0 1144 and parsed_datetime.minute == 0 1145 and parsed_datetime.second == 0 1146 and parsed_datetime.tzinfo is None 1147 and 8 <= len(data) <= 10 1148 ) 1149 if date_only: 1150 raise ValueError("This is a date, not a datetime") 1151 return parsed_datetime 1152 elif klass == date: 1153 if len(data) < 8: 1154 raise ValueError("This is not a date") 1155 return parse(data).date() 1156 else: 1157 converted_value = klass(data) 1158 if isinstance(data, str) and klass == float: 1159 if str(converted_value) != data: 1160 # '7' -> 7.0 -> '7.0' != '7' 1161 raise ValueError("This is not a float") 1162 return converted_value 1163 except (OverflowError, ValueError) as ex: 1164 # parse can raise OverflowError 1165 raise ApiValueError( 1166 "{0}Failed to parse {1} as {2}".format(additional_message, repr(data), klass.__name__), 1167 path_to_item=path_to_item, 1168 ) from ex
Deserializes string to primitive type.
Parameters
- data: str/int/float
- klass: str/class the class to convert to
Returns
int, float, str, bool, date, datetime
1171def get_discriminator_class(model_class, discr_name, discr_value, cls_visited): 1172 """Returns the child class specified by the discriminator. 1173 1174 Args: 1175 model_class (OpenApiModel): the model class. 1176 discr_name (string): the name of the discriminator property. 1177 discr_value (any): the discriminator value. 1178 cls_visited (list): list of model classes that have been visited. 1179 Used to determine the discriminator class without 1180 visiting circular references indefinitely. 1181 1182 Returns: 1183 used_model_class (class/None): the chosen child class that will be used 1184 to deserialize the data, for example dog.Dog. 1185 If a class is not found, None is returned. 1186 """ 1187 1188 if model_class in cls_visited: 1189 # The class has already been visited and no suitable class was found. 1190 return None 1191 cls_visited.append(model_class) 1192 used_model_class = None 1193 if discr_name in model_class.discriminator: 1194 class_name_to_discr_class = model_class.discriminator[discr_name] 1195 used_model_class = class_name_to_discr_class.get(discr_value) 1196 if used_model_class is None: 1197 # We didn't find a discriminated class in class_name_to_discr_class. 1198 # So look in the ancestor or descendant discriminators 1199 # The discriminator mapping may exist in a descendant (anyOf, oneOf) 1200 # or ancestor (allOf). 1201 # Ancestor example: in the GrandparentAnimal -> ParentPet -> ChildCat 1202 # hierarchy, the discriminator mappings may be defined at any level 1203 # in the hierarchy. 1204 # Descendant example: mammal -> whale/zebra/Pig -> BasquePig/DanishPig 1205 # if we try to make BasquePig from mammal, we need to travel through 1206 # the oneOf descendant discriminators to find BasquePig 1207 descendant_classes = model_class._composed_schemas.get("oneOf", ()) + model_class._composed_schemas.get( 1208 "anyOf", () 1209 ) 1210 ancestor_classes = model_class._composed_schemas.get("allOf", ()) 1211 possible_classes = descendant_classes + ancestor_classes 1212 for cls in possible_classes: 1213 # Check if the schema has inherited discriminators. 1214 if hasattr(cls, "discriminator") and cls.discriminator is not None: 1215 used_model_class = get_discriminator_class(cls, discr_name, discr_value, cls_visited) 1216 if used_model_class is not None: 1217 return used_model_class 1218 return used_model_class
Returns the child class specified by the discriminator.
Arguments:
- model_class (OpenApiModel): the model class.
- discr_name (string): the name of the discriminator property.
- discr_value (any): the discriminator value.
- cls_visited (list): list of model classes that have been visited. Used to determine the discriminator class without visiting circular references indefinitely.
Returns:
used_model_class (class/None): the chosen child class that will be used to deserialize the data, for example dog.Dog. If a class is not found, None is returned.
1221def deserialize_model(model_data, model_class, path_to_item, check_type, configuration, spec_property_naming): 1222 """Deserializes model_data to model instance. 1223 1224 Args: 1225 model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model 1226 model_class (OpenApiModel): the model class 1227 path_to_item (list): path to the model in the received data 1228 check_type (bool): whether to check the data tupe for the values in 1229 the model 1230 configuration (Configuration): the instance to use to convert files 1231 spec_property_naming (bool): True if the variable names in the input 1232 data are serialized names as specified in the OpenAPI document. 1233 False if the variables names in the input data are python 1234 variable names in PEP-8 snake case. 1235 1236 Returns: 1237 model instance 1238 1239 Raise: 1240 ApiTypeError 1241 ApiValueError 1242 ApiKeyError 1243 """ 1244 1245 kw_args = dict( 1246 _check_type=check_type, 1247 _path_to_item=path_to_item, 1248 _configuration=configuration, 1249 _spec_property_naming=spec_property_naming, 1250 ) 1251 1252 if issubclass(model_class, ModelSimple): 1253 return model_class._new_from_openapi_data(model_data, **kw_args) 1254 elif isinstance(model_data, list): 1255 return model_class._new_from_openapi_data(*model_data, **kw_args) 1256 if isinstance(model_data, dict): 1257 kw_args.update(model_data) 1258 return model_class._new_from_openapi_data(**kw_args) 1259 elif isinstance(model_data, PRIMITIVE_TYPES): 1260 return model_class._new_from_openapi_data(model_data, **kw_args)
Deserializes model_data to model instance.
Arguments:
- model_data (int/str/float/bool/none_type/list/dict): data to instantiate the model
- model_class (OpenApiModel): the model class
- path_to_item (list): path to the model in the received data
- check_type (bool): whether to check the data tupe for the values in the model
- configuration (Configuration): the instance to use to convert files
- spec_property_naming (bool): True if the variable names in the input data are serialized names as specified in the OpenAPI document. False if the variables names in the input data are python variable names in PEP-8 snake case.
Returns:
model instance
Raise:
ApiTypeError ApiValueError ApiKeyError
1263def deserialize_file(response_data, configuration, content_disposition=None): 1264 """Deserializes body to file 1265 1266 Saves response body into a file in a temporary folder, 1267 using the filename from the `Content-Disposition` header if provided. 1268 1269 Args: 1270 param response_data (str): the file data to write 1271 configuration (Configuration): the instance to use to convert files 1272 1273 Keyword Args: 1274 content_disposition (str): the value of the Content-Disposition 1275 header 1276 1277 Returns: 1278 (file_type): the deserialized file which is open 1279 The user is responsible for closing and reading the file 1280 """ 1281 fd, path = tempfile.mkstemp(dir=configuration.temp_folder_path) 1282 os.close(fd) 1283 os.remove(path) 1284 1285 if content_disposition: 1286 filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?', content_disposition).group(1) 1287 path = os.path.join(os.path.dirname(path), filename) 1288 1289 with open(path, "wb") as f: 1290 if isinstance(response_data, str): 1291 # change str to bytes so we can write it 1292 response_data = response_data.encode("utf-8") 1293 f.write(response_data) 1294 1295 f = open(path, "rb") 1296 return f
Deserializes body to file
Saves response body into a file in a temporary folder,
using the filename from the Content-Disposition header if provided.
Arguments:
- param response_data (str): the file data to write
- configuration (Configuration): the instance to use to convert files
Keyword Args:
content_disposition (str): the value of the Content-Disposition header
Returns:
(file_type): the deserialized file which is open The user is responsible for closing and reading the file
1299def attempt_convert_item( 1300 input_value, 1301 valid_classes, 1302 path_to_item, 1303 configuration, 1304 spec_property_naming, 1305 key_type=False, 1306 must_convert=False, 1307 check_type=True, 1308): 1309 """ 1310 Args: 1311 input_value (any): the data to convert 1312 valid_classes (any): the classes that are valid 1313 path_to_item (list): the path to the item to convert 1314 configuration (Configuration): the instance to use to convert files 1315 spec_property_naming (bool): True if the variable names in the input 1316 data are serialized names as specified in the OpenAPI document. 1317 False if the variables names in the input data are python 1318 variable names in PEP-8 snake case. 1319 key_type (bool): if True we need to convert a key type (not supported) 1320 must_convert (bool): if True we must convert 1321 check_type (bool): if True we check the type or the returned data in 1322 ModelComposed/ModelNormal/ModelSimple instances 1323 1324 Returns: 1325 instance (any) the fixed item 1326 1327 Raises: 1328 ApiTypeError 1329 ApiValueError 1330 ApiKeyError 1331 """ 1332 valid_classes_ordered = order_response_types(valid_classes) 1333 valid_classes_coercible = remove_uncoercible(valid_classes_ordered, input_value, spec_property_naming) 1334 if not valid_classes_coercible or key_type: 1335 # we do not handle keytype errors, json will take care 1336 # of this for us 1337 if configuration is None or not configuration.discard_unknown_keys: 1338 raise get_type_error(input_value, path_to_item, valid_classes, key_type=key_type) 1339 for valid_class in valid_classes_coercible: 1340 try: 1341 if issubclass(valid_class, OpenApiModel): 1342 return deserialize_model( 1343 input_value, valid_class, path_to_item, check_type, configuration, spec_property_naming 1344 ) 1345 elif valid_class == file_type: 1346 return deserialize_file(input_value, configuration) 1347 return deserialize_primitive(input_value, valid_class, path_to_item) 1348 except (ApiTypeError, ApiValueError, ApiKeyError) as conversion_exc: 1349 if must_convert: 1350 raise conversion_exc 1351 # if we have conversion errors when must_convert == False 1352 # we ignore the exception and move on to the next class 1353 continue 1354 # we were unable to convert, must_convert == False 1355 return input_value
Arguments:
- input_value (any): the data to convert
- valid_classes (any): the classes that are valid
- path_to_item (list): the path to the item to convert
- configuration (Configuration): the instance to use to convert files
- spec_property_naming (bool): True if the variable names in the input data are serialized names as specified in the OpenAPI document. False if the variables names in the input data are python variable names in PEP-8 snake case.
- key_type (bool): if True we need to convert a key type (not supported)
- must_convert (bool): if True we must convert
- check_type (bool): if True we check the type or the returned data in ModelComposed/ModelNormal/ModelSimple instances
Returns:
instance (any) the fixed item
Raises:
- ApiTypeError
- ApiValueError
- ApiKeyError
1358def is_type_nullable(input_type): 1359 """ 1360 Returns true if None is an allowed value for the specified input_type. 1361 1362 A type is nullable if at least one of the following conditions is true: 1363 1. The OAS 'nullable' attribute has been specified, 1364 1. The type is the 'null' type, 1365 1. The type is a anyOf/oneOf composed schema, and a child schema is 1366 the 'null' type. 1367 Args: 1368 input_type (type): the class of the input_value that we are 1369 checking 1370 Returns: 1371 bool 1372 """ 1373 if input_type is none_type: 1374 return True 1375 if issubclass(input_type, OpenApiModel) and input_type._nullable: 1376 return True 1377 if issubclass(input_type, ModelComposed): 1378 # If oneOf/anyOf, check if the 'null' type is one of the allowed types. 1379 for t in input_type._composed_schemas.get("oneOf", ()): 1380 if is_type_nullable(t): 1381 return True 1382 for t in input_type._composed_schemas.get("anyOf", ()): 1383 if is_type_nullable(t): 1384 return True 1385 return False
Returns true if None is an allowed value for the specified input_type.
A type is nullable if at least one of the following conditions is true:
- The OAS 'nullable' attribute has been specified,
- The type is the 'null' type,
- The type is a anyOf/oneOf composed schema, and a child schema is the 'null' type.
Arguments:
- input_type (type): the class of the input_value that we are checking
Returns:
bool
1388def is_valid_type(input_class_simple, valid_classes): 1389 """ 1390 Args: 1391 input_class_simple (class): the class of the input_value that we are 1392 checking 1393 valid_classes (tuple): the valid classes that the current item 1394 should be 1395 Returns: 1396 bool 1397 """ 1398 valid_type = input_class_simple in valid_classes 1399 if not valid_type and (issubclass(input_class_simple, OpenApiModel) or input_class_simple is none_type): 1400 for valid_class in valid_classes: 1401 if input_class_simple is none_type and is_type_nullable(valid_class): 1402 # Schema is oneOf/anyOf and the 'null' type is one of the allowed types. 1403 return True 1404 if not (issubclass(valid_class, OpenApiModel) and valid_class.discriminator): 1405 continue 1406 discr_propertyname_py = list(valid_class.discriminator.keys())[0] 1407 discriminator_classes = valid_class.discriminator[discr_propertyname_py].values() 1408 valid_type = is_valid_type(input_class_simple, discriminator_classes) 1409 if valid_type: 1410 return True 1411 return valid_type
Arguments:
- input_class_simple (class): the class of the input_value that we are checking
- valid_classes (tuple): the valid classes that the current item should be
Returns:
bool
1414def validate_and_convert_types( 1415 input_value, required_types_mixed, path_to_item, spec_property_naming, _check_type, configuration=None 1416): 1417 """Raises a TypeError is there is a problem, otherwise returns value 1418 1419 Args: 1420 input_value (any): the data to validate/convert 1421 required_types_mixed (list/dict/tuple): A list of 1422 valid classes, or a list tuples of valid classes, or a dict where 1423 the value is a tuple of value classes 1424 path_to_item: (list) the path to the data being validated 1425 this stores a list of keys or indices to get to the data being 1426 validated 1427 spec_property_naming (bool): True if the variable names in the input 1428 data are serialized names as specified in the OpenAPI document. 1429 False if the variables names in the input data are python 1430 variable names in PEP-8 snake case. 1431 _check_type: (boolean) if true, type will be checked and conversion 1432 will be attempted. 1433 configuration: (Configuration): the configuration class to use 1434 when converting file_type items. 1435 If passed, conversion will be attempted when possible 1436 If not passed, no conversions will be attempted and 1437 exceptions will be raised 1438 1439 Returns: 1440 the correctly typed value 1441 1442 Raises: 1443 ApiTypeError 1444 """ 1445 results = get_required_type_classes(required_types_mixed, spec_property_naming) 1446 valid_classes, child_req_types_by_current_type = results 1447 1448 input_class_simple = get_simple_class(input_value) 1449 valid_type = is_valid_type(input_class_simple, valid_classes) 1450 if not valid_type: 1451 if configuration: 1452 # if input_value is not valid_type try to convert it 1453 converted_instance = attempt_convert_item( 1454 input_value, 1455 valid_classes, 1456 path_to_item, 1457 configuration, 1458 spec_property_naming, 1459 key_type=False, 1460 must_convert=True, 1461 check_type=_check_type, 1462 ) 1463 return converted_instance 1464 else: 1465 raise get_type_error(input_value, path_to_item, valid_classes, key_type=False) 1466 1467 # input_value's type is in valid_classes 1468 if len(valid_classes) > 1 and configuration: 1469 # there are valid classes which are not the current class 1470 valid_classes_coercible = remove_uncoercible( 1471 valid_classes, input_value, spec_property_naming, must_convert=False 1472 ) 1473 if valid_classes_coercible: 1474 converted_instance = attempt_convert_item( 1475 input_value, 1476 valid_classes_coercible, 1477 path_to_item, 1478 configuration, 1479 spec_property_naming, 1480 key_type=False, 1481 must_convert=False, 1482 check_type=_check_type, 1483 ) 1484 return converted_instance 1485 1486 if child_req_types_by_current_type == {}: 1487 # all types are of the required types and there are no more inner 1488 # variables left to look at 1489 return input_value 1490 inner_required_types = child_req_types_by_current_type.get(type(input_value)) 1491 if inner_required_types is None: 1492 # for this type, there are not more inner variables left to look at 1493 return input_value 1494 if isinstance(input_value, list): 1495 if input_value == []: 1496 # allow an empty list 1497 return input_value 1498 for index, inner_value in enumerate(input_value): 1499 inner_path = list(path_to_item) 1500 inner_path.append(index) 1501 input_value[index] = validate_and_convert_types( 1502 inner_value, 1503 inner_required_types, 1504 inner_path, 1505 spec_property_naming, 1506 _check_type, 1507 configuration=configuration, 1508 ) 1509 elif isinstance(input_value, dict): 1510 if input_value == {}: 1511 # allow an empty dict 1512 return input_value 1513 for inner_key, inner_val in input_value.items(): 1514 inner_path = list(path_to_item) 1515 inner_path.append(inner_key) 1516 if get_simple_class(inner_key) != str: 1517 raise get_type_error(inner_key, inner_path, valid_classes, key_type=True) 1518 input_value[inner_key] = validate_and_convert_types( 1519 inner_val, 1520 inner_required_types, 1521 inner_path, 1522 spec_property_naming, 1523 _check_type, 1524 configuration=configuration, 1525 ) 1526 return input_value
Raises a TypeError is there is a problem, otherwise returns value
Arguments:
- input_value (any): the data to validate/convert
- required_types_mixed (list/dict/tuple): A list of valid classes, or a list tuples of valid classes, or a dict where the value is a tuple of value classes
- path_to_item: (list) the path to the data being validated this stores a list of keys or indices to get to the data being validated
- spec_property_naming (bool): True if the variable names in the input data are serialized names as specified in the OpenAPI document. False if the variables names in the input data are python variable names in PEP-8 snake case.
- _check_type: (boolean) if true, type will be checked and conversion will be attempted.
- configuration: (Configuration): the configuration class to use when converting file_type items. If passed, conversion will be attempted when possible If not passed, no conversions will be attempted and exceptions will be raised
Returns:
the correctly typed value
Raises:
- ApiTypeError
1529def model_to_dict(model_instance, serialize=True): 1530 """Returns the model properties as a dict 1531 1532 Args: 1533 model_instance (one of your model instances): the model instance that 1534 will be converted to a dict. 1535 1536 Keyword Args: 1537 serialize (bool): if True, the keys in the dict will be values from 1538 attribute_map 1539 """ 1540 result = {} 1541 1542 model_instances = [model_instance] 1543 if model_instance._composed_schemas: 1544 model_instances.extend(model_instance._composed_instances) 1545 seen_json_attribute_names = set() 1546 used_fallback_python_attribute_names = set() 1547 py_to_json_map = {} 1548 for model_instance in model_instances: 1549 for attr, value in model_instance._data_store.items(): 1550 if serialize: 1551 # we use get here because additional property key names do not 1552 # exist in attribute_map 1553 try: 1554 attr = model_instance.attribute_map[attr] 1555 py_to_json_map.update(model_instance.attribute_map) 1556 seen_json_attribute_names.add(attr) 1557 except KeyError: 1558 used_fallback_python_attribute_names.add(attr) 1559 if isinstance(value, list): 1560 if not value: 1561 # empty list or None 1562 result[attr] = value 1563 else: 1564 res = [] 1565 for v in value: 1566 if isinstance(v, PRIMITIVE_TYPES) or v is None: 1567 res.append(v) 1568 elif isinstance(v, ModelSimple): 1569 res.append(v.value) 1570 else: 1571 res.append(model_to_dict(v, serialize=serialize)) 1572 result[attr] = res 1573 elif isinstance(value, dict): 1574 result[attr] = dict( 1575 map( 1576 lambda item: (item[0], model_to_dict(item[1], serialize=serialize)) 1577 if hasattr(item[1], "_data_store") 1578 else item, 1579 value.items(), 1580 ) 1581 ) 1582 elif isinstance(value, ModelSimple): 1583 result[attr] = value.value 1584 elif hasattr(value, "_data_store"): 1585 result[attr] = model_to_dict(value, serialize=serialize) 1586 else: 1587 result[attr] = value 1588 if serialize: 1589 for python_key in used_fallback_python_attribute_names: 1590 json_key = py_to_json_map.get(python_key) 1591 if json_key is None: 1592 continue 1593 if python_key == json_key: 1594 continue 1595 json_key_assigned_no_need_for_python_key = json_key in seen_json_attribute_names 1596 if json_key_assigned_no_need_for_python_key: 1597 del result[python_key] 1598 1599 return result
Returns the model properties as a dict
Arguments:
- model_instance (one of your model instances): the model instance that will be converted to a dict.
Keyword Args:
serialize (bool): if True, the keys in the dict will be values from attribute_map
1602def type_error_message(var_value=None, var_name=None, valid_classes=None, key_type=None): 1603 """ 1604 Keyword Args: 1605 var_value (any): the variable which has the type_error 1606 var_name (str): the name of the variable which has the typ error 1607 valid_classes (tuple): the accepted classes for current_item's 1608 value 1609 key_type (bool): False if our value is a value in a dict 1610 True if it is a key in a dict 1611 False if our item is an item in a list 1612 """ 1613 key_or_value = "value" 1614 if key_type: 1615 key_or_value = "key" 1616 valid_classes_phrase = get_valid_classes_phrase(valid_classes) 1617 msg = "Invalid type for variable '{0}'. Required {1} type {2} and " "passed type was {3}".format( 1618 var_name, 1619 key_or_value, 1620 valid_classes_phrase, 1621 type(var_value).__name__, 1622 ) 1623 return msg
Keyword Args:
var_value (any): the variable which has the type_error var_name (str): the name of the variable which has the typ error valid_classes (tuple): the accepted classes for current_item's value key_type (bool): False if our value is a value in a dict True if it is a key in a dict False if our item is an item in a list
1626def get_valid_classes_phrase(input_classes): 1627 """Returns a string phrase describing what types are allowed""" 1628 all_classes = list(input_classes) 1629 all_classes = sorted(all_classes, key=lambda cls: cls.__name__) 1630 all_class_names = [cls.__name__ for cls in all_classes] 1631 if len(all_class_names) == 1: 1632 return "is {0}".format(all_class_names[0]) 1633 return "is one of [{0}]".format(", ".join(all_class_names))
Returns a string phrase describing what types are allowed
1636def get_allof_instances(self, model_args, constant_args): 1637 """ 1638 Args: 1639 self: the class we are handling 1640 model_args (dict): var_name to var_value 1641 used to make instances 1642 constant_args (dict): 1643 metadata arguments: 1644 _check_type 1645 _path_to_item 1646 _spec_property_naming 1647 _configuration 1648 _visited_composed_classes 1649 1650 Returns 1651 composed_instances (list) 1652 """ 1653 composed_instances = [] 1654 for allof_class in self._composed_schemas["allOf"]: 1655 try: 1656 allof_instance = allof_class(**model_args, **constant_args) 1657 composed_instances.append(allof_instance) 1658 except Exception as ex: 1659 raise ApiValueError( 1660 "Invalid inputs given to generate an instance of '%s'. The " 1661 "input data was invalid for the allOf schema '%s' in the composed " 1662 "schema '%s'. Error=%s" % (allof_class.__name__, allof_class.__name__, self.__class__.__name__, str(ex)) 1663 ) from ex 1664 return composed_instances
Arguments:
- self: the class we are handling
- model_args (dict): var_name to var_value used to make instances
- constant_args (dict): metadata arguments: _check_type _path_to_item _spec_property_naming _configuration _visited_composed_classes
Returns composed_instances (list)
1667def get_oneof_instance(cls, model_kwargs, constant_kwargs, model_arg=None): 1668 """ 1669 Find the oneOf schema that matches the input data (e.g. payload). 1670 If exactly one schema matches the input data, an instance of that schema 1671 is returned. 1672 If zero or more than one schema match the input data, an exception is raised. 1673 In OAS 3.x, the payload MUST, by validation, match exactly one of the 1674 schemas described by oneOf. 1675 1676 Args: 1677 cls: the class we are handling 1678 model_kwargs (dict): var_name to var_value 1679 The input data, e.g. the payload that must match a oneOf schema 1680 in the OpenAPI document. 1681 constant_kwargs (dict): var_name to var_value 1682 args that every model requires, including configuration, server 1683 and path to item. 1684 1685 Kwargs: 1686 model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): 1687 the value to assign to a primitive class or ModelSimple class 1688 Notes: 1689 - this is only passed in when oneOf includes types which are not object 1690 - None is used to suppress handling of model_arg, nullable models are handled in __new__ 1691 1692 Returns 1693 oneof_instance (instance) 1694 """ 1695 if len(cls._composed_schemas["oneOf"]) == 0: 1696 return None 1697 1698 oneof_instances = [] 1699 # Iterate over each oneOf schema and determine if the input data 1700 # matches the oneOf schemas. 1701 for oneof_class in cls._composed_schemas["oneOf"]: 1702 # The composed oneOf schema allows the 'null' type and the input data 1703 # is the null value. This is a OAS >= 3.1 feature. 1704 if oneof_class is none_type: 1705 # skip none_types because we are deserializing dict data. 1706 # none_type deserialization is handled in the __new__ method 1707 continue 1708 1709 single_value_input = allows_single_value_input(oneof_class) 1710 1711 try: 1712 if not single_value_input: 1713 oneof_instance = oneof_class(**model_kwargs, **constant_kwargs) 1714 else: 1715 if issubclass(oneof_class, ModelSimple): 1716 oneof_instance = oneof_class(model_arg, **constant_kwargs) 1717 elif oneof_class in PRIMITIVE_TYPES: 1718 oneof_instance = validate_and_convert_types( 1719 model_arg, 1720 (oneof_class,), 1721 constant_kwargs["_path_to_item"], 1722 constant_kwargs["_spec_property_naming"], 1723 constant_kwargs["_check_type"], 1724 configuration=constant_kwargs["_configuration"], 1725 ) 1726 oneof_instances.append(oneof_instance) 1727 except Exception: 1728 pass 1729 if len(oneof_instances) == 0: 1730 raise ApiValueError( 1731 "Invalid inputs given to generate an instance of %s. None " 1732 "of the oneOf schemas matched the input data." % cls.__name__ 1733 ) 1734 elif len(oneof_instances) > 1: 1735 raise ApiValueError( 1736 "Invalid inputs given to generate an instance of %s. Multiple " 1737 "oneOf schemas matched the inputs, but a max of one is allowed." % cls.__name__ 1738 ) 1739 return oneof_instances[0]
Find the oneOf schema that matches the input data (e.g. payload). If exactly one schema matches the input data, an instance of that schema is returned. If zero or more than one schema match the input data, an exception is raised. In OAS 3.x, the payload MUST, by validation, match exactly one of the schemas described by oneOf.
Arguments:
- cls: the class we are handling
- model_kwargs (dict): var_name to var_value The input data, e.g. the payload that must match a oneOf schema in the OpenAPI document.
- constant_kwargs (dict): var_name to var_value args that every model requires, including configuration, server and path to item.
Kwargs:
model_arg: (int, float, bool, str, date, datetime, ModelSimple, None): the value to assign to a primitive class or ModelSimple class Notes: - this is only passed in when oneOf includes types which are not object - None is used to suppress handling of model_arg, nullable models are handled in __new__
Returns oneof_instance (instance)
1742def get_anyof_instances(self, model_args, constant_args): 1743 """ 1744 Args: 1745 self: the class we are handling 1746 model_args (dict): var_name to var_value 1747 The input data, e.g. the payload that must match at least one 1748 anyOf child schema in the OpenAPI document. 1749 constant_args (dict): var_name to var_value 1750 args that every model requires, including configuration, server 1751 and path to item. 1752 1753 Returns 1754 anyof_instances (list) 1755 """ 1756 anyof_instances = [] 1757 if len(self._composed_schemas["anyOf"]) == 0: 1758 return anyof_instances 1759 1760 for anyof_class in self._composed_schemas["anyOf"]: 1761 # The composed oneOf schema allows the 'null' type and the input data 1762 # is the null value. This is a OAS >= 3.1 feature. 1763 if anyof_class is none_type: 1764 # skip none_types because we are deserializing dict data. 1765 # none_type deserialization is handled in the __new__ method 1766 continue 1767 1768 try: 1769 anyof_instance = anyof_class(**model_args, **constant_args) 1770 anyof_instances.append(anyof_instance) 1771 except Exception: 1772 pass 1773 if len(anyof_instances) == 0: 1774 raise ApiValueError( 1775 "Invalid inputs given to generate an instance of %s. None of the " 1776 "anyOf schemas matched the inputs." % self.__class__.__name__ 1777 ) 1778 return anyof_instances
Arguments:
- self: the class we are handling
- model_args (dict): var_name to var_value The input data, e.g. the payload that must match at least one anyOf child schema in the OpenAPI document.
- constant_args (dict): var_name to var_value args that every model requires, including configuration, server and path to item.
Returns anyof_instances (list)
1781def get_discarded_args(self, composed_instances, model_args): 1782 """ 1783 Gathers the args that were discarded by configuration.discard_unknown_keys 1784 """ 1785 model_arg_keys = model_args.keys() 1786 discarded_args = set() 1787 # arguments passed to self were already converted to python names 1788 # before __init__ was called 1789 for instance in composed_instances: 1790 if instance.__class__ in self._composed_schemas["allOf"]: 1791 try: 1792 keys = instance.to_dict().keys() 1793 discarded_keys = model_args - keys 1794 discarded_args.update(discarded_keys) 1795 except Exception: 1796 # allOf integer schema will throw exception 1797 pass 1798 else: 1799 try: 1800 all_keys = set(model_to_dict(instance, serialize=False).keys()) 1801 js_keys = model_to_dict(instance, serialize=True).keys() 1802 all_keys.update(js_keys) 1803 discarded_keys = model_arg_keys - all_keys 1804 discarded_args.update(discarded_keys) 1805 except Exception: 1806 # allOf integer schema will throw exception 1807 pass 1808 return discarded_args
Gathers the args that were discarded by configuration.discard_unknown_keys
1811def validate_get_composed_info(constant_args, model_args, self): 1812 """ 1813 For composed schemas, generate schema instances for 1814 all schemas in the oneOf/anyOf/allOf definition. If additional 1815 properties are allowed, also assign those properties on 1816 all matched schemas that contain additionalProperties. 1817 Openapi schemas are python classes. 1818 1819 Exceptions are raised if: 1820 - 0 or > 1 oneOf schema matches the model_args input data 1821 - no anyOf schema matches the model_args input data 1822 - any of the allOf schemas do not match the model_args input data 1823 1824 Args: 1825 constant_args (dict): these are the args that every model requires 1826 model_args (dict): these are the required and optional spec args that 1827 were passed in to make this model 1828 self (class): the class that we are instantiating 1829 This class contains self._composed_schemas 1830 1831 Returns: 1832 composed_info (list): length three 1833 composed_instances (list): the composed instances which are not 1834 self 1835 var_name_to_model_instances (dict): a dict going from var_name 1836 to the model_instance which holds that var_name 1837 the model_instance may be self or an instance of one of the 1838 classes in self.composed_instances() 1839 additional_properties_model_instances (list): a list of the 1840 model instances which have the property 1841 additional_properties_type. This list can include self 1842 """ 1843 # create composed_instances 1844 composed_instances = [] 1845 allof_instances = get_allof_instances(self, model_args, constant_args) 1846 composed_instances.extend(allof_instances) 1847 oneof_instance = get_oneof_instance(self.__class__, model_args, constant_args) 1848 if oneof_instance is not None: 1849 composed_instances.append(oneof_instance) 1850 anyof_instances = get_anyof_instances(self, model_args, constant_args) 1851 composed_instances.extend(anyof_instances) 1852 """ 1853 set additional_properties_model_instances 1854 additional properties must be evaluated at the schema level 1855 so self's additional properties are most important 1856 If self is a composed schema with: 1857 - no properties defined in self 1858 - additionalProperties: False 1859 Then for object payloads every property is an additional property 1860 and they are not allowed, so only empty dict is allowed 1861 1862 Properties must be set on all matching schemas 1863 so when a property is assigned toa composed instance, it must be set on all 1864 composed instances regardless of additionalProperties presence 1865 keeping it to prevent breaking changes in v5.0.1 1866 TODO remove cls._additional_properties_model_instances in 6.0.0 1867 """ 1868 additional_properties_model_instances = [] 1869 if self.additional_properties_type is not None: 1870 additional_properties_model_instances = [self] 1871 1872 """ 1873 no need to set properties on self in here, they will be set in __init__ 1874 By here all composed schema oneOf/anyOf/allOf instances have their properties set using 1875 model_args 1876 """ 1877 discarded_args = get_discarded_args(self, composed_instances, model_args) 1878 1879 # map variable names to composed_instances 1880 var_name_to_model_instances = {} 1881 for prop_name in model_args: 1882 if prop_name not in discarded_args: 1883 var_name_to_model_instances[prop_name] = [self] + composed_instances 1884 1885 return [composed_instances, var_name_to_model_instances, additional_properties_model_instances, discarded_args]
For composed schemas, generate schema instances for all schemas in the oneOf/anyOf/allOf definition. If additional properties are allowed, also assign those properties on all matched schemas that contain additionalProperties. Openapi schemas are python classes.
Exceptions are raised if:
- 0 or > 1 oneOf schema matches the model_args input data
- no anyOf schema matches the model_args input data
- any of the allOf schemas do not match the model_args input data
Arguments:
- constant_args (dict): these are the args that every model requires
- model_args (dict): these are the required and optional spec args that were passed in to make this model
- self (class): the class that we are instantiating This class contains self._composed_schemas
Returns:
composed_info (list): length three composed_instances (list): the composed instances which are not self var_name_to_model_instances (dict): a dict going from var_name to the model_instance which holds that var_name the model_instance may be self or an instance of one of the classes in self.composed_instances() additional_properties_model_instances (list): a list of the model instances which have the property additional_properties_type. This list can include self