You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm writing a custom vector class for a specific type of simulation I'm working on. This class implements, amongst other operations, __add__ and __radd__ operations, with numpy floating point arguments. However, for some reason, Mypy flags a np.floating type as not having a callable __add__ method.
To Reproduce
Minimal example.py:
from __future__ import annotations
import numpy as np
class my_float:
def __radd__(self, other: np.float32) -> my_float:
return my_float()
Running mypy example.py:
example.py:6: error: Forward operator "__add__" is not callable
Found 1 error in 1 file (checked 1 source file)
More investigation
At first, I thought this was perhaps a bug in numpy, but I am not convinced. Numpy, even though all arithmetic operators are based on ufuncs written in C, provides accurate static typing stubs for them. Mypy can resolve these correctly:
a = np.float32()
b = np.float32()
reveal_type(a + b) # Revealed type is "numpy.floating[numpy.typing._32Bit]"
I believe the problem lies in that for the np.floating class and derivatives, __add__ (and other operators) are not directly defined as (overloaded) methods, but hinted as __add__: _FloatOp[_NBit1]
I had a look in more detail in how this results in mypy not resolving the operator as callable, by cloning the current mypy master and digging into it. In the end, I believe the problem lies in the mypy method check_reverse_op_method, more specifically in the call here:
The forward_type is used in a call to check_overlapping_op_methods, and since there is no check for isinstance(forward_item, Instance), we end up here in the case of my original example.py:
One (perhaps dirty) fix would be to add a simple test for an instance right after line 1293 in check_overlapping_op_methods and test if it is callable, and resolve that:
if isinstance(forward_item, Instance):
if forward_item.has_readable_member('__call__'):
forward_item = self.expr_checker.analyze_external_member_access('__call__',
forward_item,
context)
This fixes the issue. However, I do not know enough about mypy internals to know why the analyze_external_member_access call does not resolve the (overloaded) __call__ method in the first place. I have tried to get it to return an instance with some non-protocol classes with overloaded __call__ methods, but I cannot easily reproduce this issue.
Any other suggestions? Or is this perhaps an implementation error on the part of Numpy?
Apologies if my issue submission is too long, but since I already did some digging I thought to share it as well.
Your Environment
Mypy version used: 0.92
Numpy version used: 1.21.4
Python version used: 3.9.7
The text was updated successfully, but these errors were encountered:
I have a pagination class with a generic method that uses a protocol for a callable definition. However, when I supply a method, MyPy expects a class with a __call__ method, not a function.
Something similar occurs with another callable protocol, but this time without generic arguments.
Bug Report
I'm writing a custom vector class for a specific type of simulation I'm working on. This class implements, amongst other operations,
__add__
and__radd__
operations, with numpy floating point arguments. However, for some reason, Mypy flags anp.floating
type as not having a callable__add__
method.To Reproduce
Minimal
example.py
:Running
mypy example.py
:More investigation
At first, I thought this was perhaps a bug in numpy, but I am not convinced. Numpy, even though all arithmetic operators are based on ufuncs written in C, provides accurate static typing stubs for them. Mypy can resolve these correctly:
I believe the problem lies in that for the
np.floating
class and derivatives,__add__
(and other operators) are not directly defined as (overloaded) methods, but hinted as__add__: _FloatOp[_NBit1]
Here,
I had a look in more detail in how this results in mypy not resolving the operator as callable, by cloning the current mypy master and digging into it. In the end, I believe the problem lies in the mypy method
check_reverse_op_method
, more specifically in the call here:mypy/mypy/checker.py
Line 1240 in 2189714
Instead of returning some function-like variable, it only returns an instance of type:
str(forward_type) = numpy.typing._callable._FloatOp[numpy.typing._32Bit*]
This is different than for example changing in my
example.py
the argument toother: float
, which gives:str(forward_type) = def (builtins.float) -> builtins.float
The
forward_type
is used in a call tocheck_overlapping_op_methods
, and since there is no check forisinstance(forward_item, Instance)
, we end up here in the case of my originalexample.py
:mypy/mypy/checker.py
Line 1306 in 2189714
resulting in the error as in my example.
One (perhaps dirty) fix would be to add a simple test for an instance right after line 1293 in
check_overlapping_op_methods
and test if it is callable, and resolve that:This fixes the issue. However, I do not know enough about mypy internals to know why the
analyze_external_member_access
call does not resolve the (overloaded)__call__
method in the first place. I have tried to get it to return an instance with some non-protocol classes with overloaded__call__
methods, but I cannot easily reproduce this issue.Any other suggestions? Or is this perhaps an implementation error on the part of Numpy?
Apologies if my issue submission is too long, but since I already did some digging I thought to share it as well.
Your Environment
The text was updated successfully, but these errors were encountered: