Skip to content

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

Open
numpy/numpy
#29092

Description

@bersbersbers

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

Activity

  1. iwakitakuma33 commented on May 30, 2025

    @iwakitakuma33

    @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
    
  2. bersbersbers commented on May 30, 2025

    @bersbersbers
    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
  3. jorenham commented on May 30, 2025

    @jorenham
    Contributor

    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

  4. bersbersbers commented on May 30, 2025

    @bersbersbers
    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.

  5. jorenham commented on Jun 2, 2025

    @jorenham
    Contributor

    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.

  6. david-zwicker commented on Sep 30, 2025

    @david-zwicker

    Is this fixed now? I run into a similar problem with numpy version 2.3.3. The only difference is that I use np.floating and np.number, which mypy still seems to complain about for __iadd__ and __isub__. The linked PR does not seem to address these cases.

  7. jorenham commented on Sep 30, 2025

    @jorenham
    Contributor

    Is this fixed now? I run into a similar problem with numpy version 2.3.3. The only difference is that I use np.floating and np.number, which mypy still seems to complain about for __iadd__ and __isub__. The linked PR does not seem to address these cases.

    Ah I didn't realize that this was also affecting number and floating, since that wasn't mentioned in the OP. I'll try to get those ones fixed as well then.

  8. david-zwicker commented on Sep 30, 2025

    @david-zwicker

    I guess the whole numpy type hierarchy is in principle affected. It would be great if there was a more systematic way to include these type hints (instead of spelling out each alternative individually).

  9. jorenham commented on Sep 30, 2025

    @jorenham
    Contributor

    It would be great if there was a more systematic way to include these type hints (instead of spelling out each alternative individually).

    There is :) I've implemented it in https://github.com/numpy/numtype (not in numpy itself because that'd be backwards incompatible)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions