138 lines
4.3 KiB
Plaintext
138 lines
4.3 KiB
Plaintext
This rule raises an issue when a special method raises a `NotImplementedError` instead of returning `NotImplemented`.
|
|
|
|
== Why is this an issue?
|
|
|
|
In Python, special methods corresponding to numeric operators and rich comparison operators should return ``++NotImplemented++`` when the operation is not supported.
|
|
|
|
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)++``. If these special methods were to raise `NotImplementedError`, the callers would not catch the exception and the reverse operation would not be called.
|
|
|
|
Below is the list of special methods this rule applies to:
|
|
|
|
* ``++__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)``
|
|
|
|
|
|
== How to fix it
|
|
|
|
Make sure special methods return `NotImplemented` instead of raising a `NotImplementedError`.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,python,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
class MyClass:
|
|
def __add__(self, other):
|
|
raise NotImplementedError() # Noncompliant: the exception will be propagated
|
|
def __radd__(self, other):
|
|
raise NotImplementedError() # Noncompliant: the exception will be propagated
|
|
|
|
class MyOtherClass:
|
|
def __add__(self, other):
|
|
return 42
|
|
def __radd__(self, other):
|
|
return 42
|
|
|
|
MyClass() + MyOtherClass() # This will raise NotImplementedError
|
|
----
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
[source,python,diff-id=1,diff-type=compliant]
|
|
----
|
|
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
|
|
----
|
|
|
|
=== Pitfalls
|
|
|
|
The ``++__length_hint__++`` special method also requires to return a `NotImplemented`. Its behavior differs from the other methods, because when it returns `NotImplemented`, a default value will be returned instead. See https://peps.python.org/pep-0424/[PEP 424] for more information.
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* 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]
|
|
|
|
=== Standards
|
|
|
|
* PEP 424 - https://peps.python.org/pep-0424/[A method exposing ``++__length_hint__++``]
|
|
|
|
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[]
|