Repository: python/mypy

Issue:
Arguments in `__truediv__` and `__itruediv__` is not specified as positional-only
When I name a function `__div__` or `__idiv__`, it seems to have different type signature from normal functions.
Is it intended behavior?


## Code to reproduce

```
$ cat main.py
class A:
    def f(self, other: int) -> int:
        pass

    def __div__(self, other: int) -> int:
        pass

    def __truediv__(self, other: int) -> int:
        pass

    def __idiv__(self, other: int) -> int:
        pass

    def __itruediv__(self, other: int) -> int:
        pass


reveal_type(A.f)
reveal_type(A.__div__)
reveal_type(A.__truediv__)
reveal_type(A.__idiv__)
reveal_type(A.__itruediv__)

$ mypy main.py
main.py:18: note: Revealed type is 'def (self: main.A, other: builtins.int) -> builtins.int'
main.py:19: note: Revealed type is 'def (main.A, builtins.int) -> builtins.int'
main.py:20: note: Revealed type is 'def (self: main.A, other: builtins.int) -> builtins.int'
main.py:21: note: Revealed type is 'def (main.A, builtins.int) -> builtins.int'
main.py:22: note: Revealed type is 'def (self: main.A, other: builtins.int) -> builtins.int'

$ mypy --version
mypy 0.782
```




Discussion:
Mypy knows that these are standard operator overloads and those have positional-only parameter lists. So yes, this is intended. The anomaly here is actually `__[i]truediv__`, which ought to be treated the same way but isn't.
OK thanks, I got it. I'll rename the issue.
By the way, should I explicitly annotate that they are positional-only:

```python
class C:
    def __add__(self, other: int, /) -> int:
        ...
```

or not? If we are encouraged to do that, would we make a new option, say, `--disallow-operator-overload-with-keyword-argument`, to prevent not to use `/` in positional-only operator overloads?

There's no need to annotate them as positional-only.

Resolving patch:
diff --git a/mypy/sharedparse.py b/mypy/sharedparse.py
--- a/mypy/sharedparse.py
+++ b/mypy/sharedparse.py
@@ -61,16 +61,19 @@
     "__idiv__",
     "__ifloordiv__",
     "__ilshift__",
+    "__imatmul__",
     "__imod__",
     "__imul__",
     "__ior__",
     "__ipow__",
     "__irshift__",
     "__isub__",
+    "__itruediv__",
     "__ixor__",
     "__le__",
     "__lshift__",
     "__lt__",
+    "__matmul__",
     "__mod__",
     "__mul__",
     "__ne__",
@@ -81,6 +84,7 @@
     "__rdiv__",
     "__rfloordiv__",
     "__rlshift__",
+    "__rmatmul__",
     "__rmod__",
     "__rmul__",
     "__ror__",
@@ -88,8 +92,10 @@
     "__rrshift__",
     "__rshift__",
     "__rsub__",
+    "__rtruediv__",
     "__rxor__",
     "__sub__",
+    "__truediv__",
     "__xor__",
 }  # type: Final
 


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -1815,6 +1815,16 @@ reveal_type(B() + A())  # N: Revealed type is '__main__.A'
 reveal_type(A() + B())  # N: Revealed type is '__main__.A'
 [builtins fixtures/isinstance.pyi]
 
+[case testBinaryOpeartorMethodPositionalArgumentsOnly]
+class A:
+    def __add__(self, other: int) -> int: pass
+    def __iadd__(self, other: int) -> int: pass
+    def __radd__(self, other: int) -> int: pass
+
+reveal_type(A.__add__)  # N: Revealed type is 'def (__main__.A, builtins.int) -> builtins.int'
+reveal_type(A.__iadd__) # N: Revealed type is 'def (__main__.A, builtins.int) -> builtins.int'
+reveal_type(A.__radd__) # N: Revealed type is 'def (__main__.A, builtins.int) -> builtins.int'
+
 [case testOperatorMethodOverrideWithIdenticalOverloadedType]
 from foo import *
 [file foo.pyi]