This rule raises an issue when a special method returns an object of an unexpected type.

== Why is this an issue?

Python allows developers to customize how code is interpreted by defining special methods (also called magic methods). For example, it is possible to define an object's own truthiness or falsiness by overriding ``++__bool__++`` method. It is invoked when the built-in ``++bool()++`` function is called on the object. The ``++bool()++`` function returns ``++True++`` or ``++False++`` based on the truth value of the object.

The Python interpreter will call these methods when performing the operation they're associated with. Each special method expects a specific return type. Calls to a special method will throw a ``++TypeError++`` if its return type is incorrect.

An issue will be raised when one of the following methods doesn't return the indicated type:

* ``++__bool__++`` method should return bool
* ``++__index__++`` method should return integer
* ``++__repr__++`` method should return string
* ``++__str__++`` method should return string
* ``++__bytes__++`` method should return bytes
* ``++__hash__++`` method should return integer
* ``++__format__++`` method should return string
* ``++__getnewargs__++`` method should return tuple
* ``++__getnewargs_ex__++`` method should return something which is of the form tuple(tuple, dict)

== How to fix it

Make sure to return a value of the same type as defined in the Python documentation for each special method.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
class MyClass:
    def __bool__(self):
        return 0 # Noncompliant: Return value of type bool here.

obj1 = MyClass()
print(bool(obj1)) # TypeError: __bool__ should return bool, returned int
----

==== Compliant solution 

[source,python,diff-id=1,diff-type=compliant]
----
class MyClass:
    def __bool__(self):
        return False

obj1 = MyClass()
print(bool(obj1))
----

== Resources

=== Documentation

* Python Data Model Documentation - https://docs.python.org/3/reference/datamodel.html#special-method-names[Special method names]
* Python Object Serialization Documentation - https://docs.python.org/3/library/pickle.html#pickling-class-instances[Pickling Class Instances]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

=== Message

==== if there is a return statement

* Return a value of type XXX here.
* Return a value of type XXX here. A tuple of two elements was expected but found tuple with YYY element(s).

==== if there is no return statement

* Return a value of type XXX in this method. Consider explicitly raising a `TypeError` if this class is not meant to support this method.
* Return a value of type XXX in this method. The method can not be a coroutine and have the `async` keyword.
* Return a value of type XXX in this method. The method can not be a generator and contain `yield` expressions.

=== Highlighting

==== if there is return statement
    Primary: the returned expressions, or the return keyword if no expressions are returned
    Secondary: method name

==== if there is no return statement at all
    Primary: method name


'''
== Comments And Links
(visible only on this page)

endif::env-github,rspecator-view[]
