rspec/rules/S5712/python/rule.adoc

123 lines
3.5 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2021-04-28 16:49:39 +02:00
In Python, special methods corresponding to numeric operators, rich comparison operators and the ``++__length_hint__++`` method should return ``++NotImplemented++`` when the operation is not supported. These methods should not raise ``++NotImplementedError++`` as callers don't expect it and won't catch this exception.
For example ``++A + B++`` is equivalent to calling ``++A.__add__(B)++``. If this binary operation is not supported by class A, ``++A.__add__(B)++`` should return ``++NotImplemented++``. The interpreter will then try the reverse operation, i.e. ``++B.__radd__(A)++``. This enables adding new operations by changing only one class instead of two.
This rule raises an issue when one of the following methods raises ``++NotImplementedError++`` instead of returning ``++NotImplemented++``:
* ++__lt__++(self, other)
* ++__le__++(self, other)
* ++__eq__++(self, other)
* ++__ne__++(self, other)
* ++__gt__++(self, other)
* ++__ge__++(self, other)
* ++__add__++(self, other)
* ++__sub__++(self, other)
* ++__mul__++(self, other)
* ++__matmul__++(self, other)
* ++__truediv__++(self, other)
* ++__floordiv__++(self, other)
* ++__mod__++(self, other)
* ++__divmod__++(self, other)
* ++__pow__++(self, other[, modulo])
* ++__lshift__++(self, other)
* ++__rshift__++(self, other)
* ++__and__++(self, other)
* ++__xor__++(self, other)
* ++__or__++(self, other)
* ++__radd__++(self, other)
* ++__rsub__++(self, other)
* ++__rmul__++(self, other)
* ++__rmatmul__++(self, other)
* ++__rtruediv__++(self, other)
* ++__rfloordiv__++(self, other)
* ++__rmod__++(self, other)
* ++__rdivmod__++(self, other)
* ++__rpow__++(self, other[, modulo])
* ++__rlshift__++(self, other)
* ++__rrshift__++(self, other)
* ++__rand__++(self, other)
* ++__rxor__++(self, other)
* ++__ror__++(self, other)
* ++__iadd__++(self, other)
* ++__isub__++(self, other)
* ++__imul__++(self, other)
* ++__imatmul__++(self, other)
* ++__itruediv__++(self, other)
* ++__ifloordiv__++(self, other)
* ++__imod__++(self, other)
* ++__ipow__++(self, other[, modulo])
* ++__ilshift__++(self, other)
* ++__irshift__++(self, other)
* ++__iand__++(self, other)
* ++__ixor__++(self, other)
* ++__ior__++(self, other)
* ++__length_hint__++(self)
=== Noncompliant code example
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,python]
2021-04-28 16:49:39 +02:00
----
class MyClass:
def __add__(self, other):
raise NotImplementedError() # Noncompliant
def __radd__(self, other):
raise NotImplementedError() # Noncompliant
class MyOtherClass:
def __add__(self, other):
return 42
def __radd__(self, other):
return 42
MyClass() + MyOtherClass() # This will raise NotImplementedError
----
=== Compliant solution
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,python]
2021-04-28 16:49:39 +02:00
----
class MyClass:
def __add__(self, other):
return NotImplemented
def __radd__(self, other):
return NotImplemented
class MyOtherClass:
def __add__(self, other):
return 42
def __radd__(self, other):
return 42
MyClass() + MyOtherClass() # This returns 42
----
== Resources
2021-04-28 16:49:39 +02:00
* Python documentation - https://docs.python.org/3/library/constants.html#NotImplemented[Built-in Constants - NotImplemented]
* Python documentation - https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations[Implementing the arithmetic operations]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Return "NotImplemented" instead of raising "NotImplementedError"
=== Highlighting
The raise statement.
endif::env-github,rspecator-view[]