docs: def _validate_opts(opts):
      '''
      Check that all of the types of values passed into the config are
      of the right types
      '''
      def format_multi_opt(valid_type):
          try:
              num_types = len(valid_type)
          except TypeError:
              # Bare type name won't have a length, return the name of the type
              # passed.
              return valid_type.__name__
          else:
              def get_types(types, type_tuple):
                  for item in type_tuple:
                      if isinstance(item, tuple):
                          get_types(types, item)
                      else:
                          try:
                              types.append(item.__name__)
                          except AttributeError:
                              log.warning(
                                  'Unable to interpret type %s while validating '
                                  'configuration', item
                              )
              types = []
              get_types(types, valid_type)
  
              ret = ', '.join(types[:-1])
              ret += ' or ' + types[-1]
              return ret
  
      errors = []
  
      err = (
          'Config option \'{0}\' with value {1} has an invalid type of {2}, a '
          '{3} is required for this option'
      )
      for key, val in six.iteritems(opts):
          if key in VALID_OPTS:
              if val is None:
                  if VALID_OPTS[key] is None:
                      continue
                  else:
                      try:
                          if None in VALID_OPTS[key]:
                              continue
                      except TypeError:
                          # VALID_OPTS[key] is not iterable and not None
                          pass
  
              if isinstance(val, VALID_OPTS[key]):
                  continue
  
              # We don't know what data type sdb will return at run-time so we
              # simply cannot check it for correctness here at start-time.
              if isinstance(val, six.string_types) and val.startswith('sdb://'):
                  continue
  
              if hasattr(VALID_OPTS[key], '__call__'):
                  try:
                      VALID_OPTS[key](val)
                      if isinstance(val, (list, dict)):
                          # We'll only get here if VALID_OPTS[key] is str or
                          # bool, and the passed value is a list/dict. Attempting
                          # to run int() or float() on a list/dict will raise an
                          # exception, but running str() or bool() on it will
                          # pass despite not being the correct type.
                          errors.append(
                              err.format(
                                  key,
                                  val,
                                  type(val).__name__,
                                  VALID_OPTS[key].__name__
                              )
                          )
                  except (TypeError, ValueError):
                      errors.append(
                          err.format(key,
                                     val,
                                     type(val).__name__,
                                     VALID_OPTS[key].__name__)
                      )
                  continue
  
              errors.append(
                  err.format(key,
                             val,
                             type(val).__name__,
                             format_multi_opt(VALID_OPTS[key]))
              )
  
      # Convert list to comma-delimited string for 'return' config option
      if isinstance(opts.get('return'), list):
          opts['return'] = ','.join(opts['return'])
  
      for error in errors:
          log.warning(error)
      if errors:
          return False
      return True