docs: def _stub_attr(obj, attr_name):
      '''
      Stub an attribute of an object. Will return an existing stub if
      there already is one.
      '''
      # Annoying circular reference requires importing here. Would like to see
      # this cleaned up. @AW
      from .mock import Mock
  
      # Check to see if this a property, this check is only for when dealing
      # with an instance. getattr will work for classes.
      is_property = False
  
      if not inspect.isclass(obj) and not inspect.ismodule(obj):
          # It's possible that the attribute is defined after initialization, and
          # so is not on the class itself.
          attr = getattr(obj.__class__, attr_name, None)
          if isinstance(attr, property):
              is_property = True
  
      if not is_property:
          attr = getattr(obj, attr_name)
  
      # Return an existing stub
      if isinstance(attr, Stub):
          return attr
  
      # If a Mock object, stub its __call__
      if isinstance(attr, Mock):
          return stub(attr.__call__)
  
      if isinstance(attr, property):
          return StubProperty(obj, attr_name)
  
      # Sadly, builtin functions and methods have the same type, so we have to
      # use the same stub class even though it's a bit ugly
      if inspect.ismodule(obj) and isinstance(attr, (types.FunctionType,
                                              types.BuiltinFunctionType,
                                              types.BuiltinMethodType)):
          return StubFunction(obj, attr_name)
  
      # In python3 unbound methods are treated as functions with no reference
      # back to the parent class and no im_* fields. We can still make unbound
      # methods work by passing these through to the stub
      if inspect.isclass(obj) and isinstance(attr, types.FunctionType):
          return StubUnboundMethod(obj, attr_name)
  
      # I thought that types.UnboundMethodType differentiated these cases but
      # apparently not.
      if isinstance(attr, types.MethodType):
          # Handle differently if unbound because it's an implicit "any instance"
          if getattr(attr, 'im_self', None) is None:
              # Handle the python3 case and py2 filter
              if hasattr(attr, '__self__'):
                  if attr.__self__ is not None:
                      return StubMethod(obj, attr_name)
              if sys.version_info.major == 2:
                  return StubUnboundMethod(attr)
          else:
              return StubMethod(obj, attr_name)
  
      if isinstance(attr, (types.BuiltinFunctionType, types.BuiltinMethodType)):
          return StubFunction(obj, attr_name)
  
      # What an absurd type this is ....
      if type(attr).__name__ == 'method-wrapper':
          return StubMethodWrapper(attr)
  
      # This is also slot_descriptor
      if type(attr).__name__ == 'wrapper_descriptor':
          return StubWrapperDescriptor(obj, attr_name)
  
      raise UnsupportedStub(
          "can't stub %s(%s) of %s", attr_name, type(attr), obj)