code: def fqn(o):
      
      parts = []
  
      # if inspect.ismethod(o):
      #     try:
      #         cls = o.im_class
      #     except AttributeError:
      #         # Python 3 eliminates im_class, substitutes __module__ and
      #         # __qualname__ to provide similar information.
      #         parts = (o.__module__, o.__qualname__)
      #     else:
      #         parts = (fqn(cls), get_classname(o))
      if hasattr(o, '__module__'):
          parts.append(o.__module__)
          parts.append(get_classname(o))
      elif inspect.ismodule(o):
          return o.__name__
      if not parts:
          raise ValueError("Invalid argument `%s`" % o)
      return ".".join(parts)
docs: Returns the fully qualified class name of an object or a class
  
      :param o: object or class
      :return: class name
  
      >>> import concurrency.fields
      >>> fqn('str')
      Traceback (most recent call last):
      ...
      ValueError: Invalid argument `str`
      >>> class A(object):
      ...     def method(self):
      ...         pass
      >>> str(fqn(A))
      'concurrency.utils.A'
  
      >>> str(fqn(A()))
      'concurrency.utils.A'
  
      >>> str(fqn(concurrency.fields))
      'concurrency.fields'
  
      >>> str(fqn(A.method))
      'concurrency.utils.A.method'