Skip to content

TYP: np.mod(npt.NDArray, Any) is Any in 2.2.2 while it wasn't in 2.2.1 #28192

Closed

Description

@bersbersbers

Describe the issue:

numpy 2.2.2 reveals a different type for np.mod(npt.NDArray, Any) than 2.2.1. This looks like a result of #28176. Please close if this is an intended change.

Reproduce the code example:

from typing import Any

import numpy as np
import numpy.typing as npt


def func(x: npt.NDArray[np.integer[Any]], y: Any) -> None:
    reveal_type(np.mod(x, y))

Error message:

2.2.1: "numpy.ndarray[builtins.tuple[builtins.int, ...], numpy.dtype[Any]]"
2.2.2: "Any"

Python and NumPy Versions:

Numpy 2.2.2 vs 2.2.1

Type-checker version and settings:

mypy 1.14.1 (compiled: yes)

Additional typing packages.

No response

Activity

  1. jorenham commented on Jan 20, 2025

    @jorenham
    Member

    Thanks for reporting this!

    This actually wasn't intentional. I'm able to reproduce this, but only with mypy, and e.g. Pyright reports the following:

    Type of "np.mod(x, y)" is "ndarray[tuple[int, ...], dtype[Any]]"
    

    But I can't explain why mypy is behaving this way. There's only one overload that return Any, and that's the first one. But as you can see, it only accepts scalar-like input, so it shouldn't be relevant in this case.

    def __call__(
    self,
    x1: _ScalarLike_co,
    x2: _ScalarLike_co,
    /,
    out: None = None,
    *,
    dtype: DTypeLike | None = None,
    **kwds: Unpack[_UFunc3Kwargs],
    ) -> Any: ...
    @overload # (array-like, array) -> array
    def __call__(
    self,
    x1: ArrayLike,
    x2: NDArray[np.generic],
    /,
    out: NDArray[np.generic] | tuple[NDArray[np.generic]] | None = None,
    *,
    dtype: DTypeLike | None = None,
    **kwds: Unpack[_UFunc3Kwargs],
    ) -> NDArray[Any]: ...
    @overload # (array, array-like) -> array
    def __call__(
    self,
    x1: NDArray[np.generic],
    x2: ArrayLike,
    /,
    out: NDArray[np.generic] | tuple[NDArray[np.generic]] | None = None,
    *,
    dtype: DTypeLike | None = None,
    **kwds: Unpack[_UFunc3Kwargs],
    ) -> NDArray[Any]: ...
    @overload # (array-like, array-like, out=array) -> array
    def __call__(
    self,
    x1: ArrayLike,
    x2: ArrayLike,
    /,
    out: NDArray[np.generic] | tuple[NDArray[np.generic]],
    *,
    dtype: DTypeLike | None = None,
    **kwds: Unpack[_UFunc3Kwargs],
    ) -> NDArray[Any]: ...
    @overload # (array-like, array-like) -> array | scalar
    def __call__(
    self,
    x1: ArrayLike,
    x2: ArrayLike,
    /,
    out: NDArray[np.generic] | tuple[NDArray[np.generic]] | None = None,
    *,
    dtype: DTypeLike | None = None,
    **kwds: Unpack[_UFunc3Kwargs],
    ) -> NDArray[Any] | Any: ...

    So I would have expected mypy to use the 2nd or 3rd overload instead, and return NDArray[Any], like it did before. Even if it went for the last overload, the return type would be NDArray[Any] | Any, which isn't the same as Any (T | Any is not reducible to Any, according to the typing spec, and is defined to have some specific meaning).


    But either way, for the majority of users, I don't expect that this will lead to any issues. In general, I would advise to avoid using Any if you can, as it can lead to undefined behavior, and using it is almost always type-unsafe.

    So if you're alright with it, I'll close this issue, as it appears to be caused by a bug in mypy (and only mypy).

  2. added
    57 - Close?Issues which may be closable unless discussion continued
    on Jan 20, 2025
  3. removed
    57 - Close?Issues which may be closable unless discussion continued
    on Jan 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions