Skip to content

"Invalid self argument" for numpy's iadd #19171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
bersbersbers opened this issue May 30, 2025 · 5 comments · Fixed by numpy/numpy#29092
Open

"Invalid self argument" for numpy's iadd #19171

bersbersbers opened this issue May 30, 2025 · 5 comments · Fixed by numpy/numpy#29092
Labels
bug mypy got something wrong topic-overloads

Comments

@bersbersbers
Copy link

To Reproduce

from typing import Any

import numpy as np
import numpy.typing as npt

def plus1(array: npt.NDArray[np.integer[Any]]) -> None:
    array += 1

Expected Behavior

No error

Actual Behavior

error: Invalid self argument "ndarray[tuple[int, ...], dtype[integer[Any]]]" to attribute function "iadd" with type "Callable[[ndarray[tuple[int, ...], dtype[numpy.bool[builtins.bool]]], _SupportsArray[dtype[numpy.bool[builtins.bool]]] | _NestedSequence[_SupportsArray[dtype[numpy.bool[builtins.bool]]]] | builtins.bool | _NestedSequence[builtins.bool]], ndarray[_ShapeT_co, _DType_co]]" [misc]

Your Environment

  • Mypy version used: 1.16.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.13.2
  • Numpy version used: 2.2.6
@bersbersbers bersbersbers added the bug mypy got something wrong label May 30, 2025
@iwakitakuma33
Copy link

@bersbersbers
Is it wrong to use union as follows?

import numpy as np
import numpy.typing as npt

from typing import Any


def plus1(array: npt.NDArray[np.uint8 | np.int8]) -> npt.NDArray[np.uint8 | np.int8]:
    return array + 1

@bersbersbers
Copy link
Author

It wouldn't be wrong necessarily, if I enumerated all possible integer types. Question is, why should I need to do that, considering that np.integer exists? :)

I also tried this, with varying success:

from typing import Any

import numpy as np
import numpy.typing as npt

# works
def plus1a(array: npt.NDArray[np.signedinteger[Any]]) -> None:
    array += 1


# works
def plus1b(array: npt.NDArray[np.unsignedinteger[Any]]) -> None:
    array += 1


# fails
def plus1c(array: npt.NDArray[np.signedinteger[Any] | np.unsignedinteger[Any]]) -> None:
    array += 1


# fails
def plus1d(array: npt.NDArray[np.integer[Any]]) -> None:
    array += 1

@jorenham
Copy link
Contributor

jorenham commented May 30, 2025

It's defined as follows in the numpy stubs:

@overload
def __iadd__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(
    self: NDArray[unsignedinteger[Any]],
    other: _ArrayLikeUInt_co | _IntLike_co,
    /,
) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[signedinteger[Any]], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[float64], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[floating[Any]], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[complex128], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[complexfloating[Any]], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[timedelta64], other: _ArrayLikeTD64_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[datetime64], other: _ArrayLikeTD64_co, /) -> ndarray[_ShapeT_co, _DType_co]: ...
@overload
def __iadd__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DType_co]: ...

https://github.com/numpy/numpy/blob/2b686f659642080e2fc708719385de6e8be0955f/numpy/__init__.pyi#L3334-L3357

So since there is no self: integer, self: number, or self: generic, mypy rejects this call to __iadd__. Mypy 1.15.0 and 1.16.0 behave in the same way, so this is not a mypy regression or something.

It's worth noting that pyright does not reject this. I'm guessing that it falls back to __add__ if the overloads of __iadd__ are exhausted.

But even so, I agree that your example should be accepted by all type-checkers. I'll try to get this fixed before the upcoming numpy 2.3 release, but it might not make it in time (2.3 is about to be released).


edit: see numpy/numpy#29092

@bersbersbers
Copy link
Author

@jorenham thanks for taking care of this. Just one bit:

Mypy 1.15.0 and 1.16.0 behave in the same way

I disagree at least partially - at least in my code base, this issue started appearing after 1.16.0 was released, and going back to 1.15.0 fixes it. Maybe my MWE above also triggers something in 1.15, but something definitely changed with 1.16.

@jorenham
Copy link
Contributor

jorenham commented Jun 2, 2025

I'll try to get this fixed before the upcoming numpy 2.3 release, but it might not make it in time (2.3 is about to be released).

This will be fixed in the upcoming numpy 2.3.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-overloads
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants