github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/clients/python-legacy/lakefs_client/model_utils.py (about)

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