docs: def check_signature(object_name, reference_object, other_object):
      """
      Given a reference class or function check if an other class or function
      could be substituted without causing any instantiation/usage issues.
  
      :param object_name: the name of the object being checked.
      :type object_name: string
      :param reference_object: the reference class or function.
      :type reference_object: class/function
      :param other_object: the other class or function to be checked.
      :type other_object: class/function
      :raise AutodiscoveryCheckerError: in case the other object is not
          compatible with the reference object.
      """
      try:
          if inspect.isclass(reference_object):
              # if the reference_object is a class
              if inspect.isclass(other_object):
                  # if the other_object is a class
                  if issubclass(other_object, reference_object):
                      # if the other_object is derived from the reference we
                      # should check for all the method in the former that
                      # exists in the the latter, wethever they recursively have
                      # the same signature.
                      reference_object_map = dict(
                          inspect.getmembers(reference_object,
                                             inspect.isroutine))
                      for other_method_name, other_method_code in \
                              inspect.getmembers(other_object,
                                                 inspect.isroutine):
                          if other_method_name in reference_object_map:
                              check_signature(
                                  object_name,
                                  reference_object_map[other_method_name],
                                  other_method_code)
                  else:
                      # if the other_object is not derived from the
                      # reference_object then all the method declared in the
                      # latter should exist in the former and they should
                      # recursively have the same signature.
                      other_object_map = dict(
                          inspect.getmembers(other_object, inspect.isroutine))
                      for reference_method_name, reference_method_code in \
                          inspect.getmembers(
                              reference_object, inspect.isroutine):
                          if reference_method_name in other_object_map:
                              check_signature(
                                  object_name, reference_method_code,
                                  other_method_code)
                          else:
                              raise AutodiscoveryCheckerError(
                                  '"%s", which'
                                  ' exists in the reference class, does not'
                                  ' exist in the other class, and the reference'
                                  ' class is not an anchestor of the other' %
                                  reference_method_name)
              else:
                  # We are comparing apples and oranges!
                  raise AutodiscoveryCheckerError(
                      "%s (the reference object)"
                      " is a class while %s (the other object) is not a class" %
                      (reference_object, other_object))
          elif inspect.isroutine(reference_object):
              # if the reference_object is a function
              if inspect.isroutine(other_object):
                  # if the other_object is a function we will compare the
                  # reference_object and other_object function signautre i.e.
                  # their parameters.
                  reference_args, reference_varargs, reference_varkw, \
                      reference_defaults = inspect.getargspec(reference_object)
                  other_args, other_varargs, other_varkw, \
                      other_defaults = inspect.getargspec(other_object)
                  # We normalize the reference_defaults to be a list
                  if reference_defaults is not None:
                      reference_defaults = list(reference_defaults)
                  else:
                      reference_defaults = []
  
                  # We normalize the other_defaults to be a list
                  if other_defaults is not None:
                      other_defaults = list(other_defaults)
                  else:
                      other_defaults = []
  
                  # Check for presence of missing parameters in other function
                  if not (other_varargs or other_varkw):
                      for reference_arg in reference_args:
                          if reference_arg not in other_args:
                              raise AutodiscoveryCheckerError(
                                  'Argument "%s"'
                                  ' in reference function %s does not exist in'
                                  ' the other function %s' %
                                  (reference_arg, reference_object, other_object)
                              )
  
                  # Check for presence of additional parameters in other
                  # function
                  if not (reference_varargs or reference_varkw):
                      for other_arg in other_args:
                          if other_arg not in reference_args:
                              raise AutodiscoveryCheckerError(
                                  'Argument "%s"'
                                  ' in other function %s does not exist in the'
                                  ' reference function %s' %
                                  (other_arg, other_object, reference_object))
  
                  # Check sorting of arguments
                  for reference_arg, other_arg in map(
                          None, reference_args, other_args):
                      if not((reference_arg == other_arg) or
                             (reference_arg is None and
                              (reference_varargs or reference_varkw)) or
                             (other_arg is None and
                              (other_args or other_varargs))):
                          raise AutodiscoveryCheckerError(
                              'Argument "%s" in'
                              ' the other function is in the position of'
                              ' argument "%s" in the reference function, i.e.'
                              ' the order of arguments is not respected' %
                              (other_arg, reference_arg))
  
                  if len(reference_defaults) != len(other_defaults) and not (
                          reference_args or reference_varargs or
                          other_args or other_varargs):
                      raise AutodiscoveryCheckerError(
                          "Default parameters in"
                          " the other function are not corresponding to the"
                          " default of parameters of the reference function")
              else:
                  # We are comparing apples and oranges!
                  raise AutodiscoveryCheckerError(
                      '%s (the reference object)'
                      ' is a function while %s (the other object) is not a'
                      ' function' % (reference_object, other_object))
      except AutodiscoveryCheckerError as err:
          try:
              sourcefile = inspect.getsourcefile(other_object)
              sourceline = inspect.getsourcelines(other_object)[1]
          except IOError:
              # other_object is not loaded from a real file
              sourcefile = 'N/A'
              sourceline = 'N/A'
          raise AutodiscoveryCheckerError(
              'Error in checking signature for'
              ' "%s" as defined at "%s" (line %s): %s' %
              (object_name, sourcefile, sourceline, err))