python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.4k stars 2.82k forks source link

mypy warning on numpy.apply_along_axis #17993

Open asarkar opened 1 week ago

asarkar commented 1 week ago

Bug Report

mypy_arg_type.py:

import numpy as np
from numpy.typing import NDArray
import random

def winner(_: NDArray[np.bytes_]) -> bytes | None:
    return b"." if bool(random.randint(0, 1)) else None

board = np.full((2, 2), ".", "|S1")
for w in np.apply_along_axis(winner, 0, board):
    print(w)

>> python mypy_arg_type.py

b'.'
None

>> mypy mypy_arg_type.py

mypy_arg_type.py:9: error: Argument 1 to "apply_along_axis" has incompatible type "Callable[[ndarray[Any, dtype[bytes_]]], bytes | None]"; expected "Callable[[ndarray[Any, dtype[Any]]], _SupportsArray[dtype[Never]] | _NestedSequence[_SupportsArray[dtype[Never]]]]"  [arg-type]
mypy_arg_type.py:9: note: This is likely because "winner" has named arguments: "_". Consider marking them positional-only
Found 1 error in 1 file (checked 1 source file)

Expected Behavior

No violations.

Actual Behavior

Shown above. Making function winner positional-only makes no difference, except that suggestion is gone.

Your Environment

brianschubert commented 4 days ago

It looks like you don’t have Numpy’s mypy plugin enabled. Does the error persist if you enable the plugin?

[tool.mypy]
plugins = ["numpy.typing.mypy_plugin"]

In any case, it’s not obvious to me whether this is a problem with mypy, a problem with Numpy’s stubs / plugin, or problem with your code. The people best suited to tell which it is would probably be the Numpy folks. I’d recommend reporting this on their tracker and posting back here if it becomes clear that this is a mypy problem (or not).

asarkar commented 4 days ago

@brianschubert I can take it to the numpy folks and see what they have to say, but the reason I started here is that I found that returning a blank byte b”” instead of None gets rid of the error. This suggests that mypy is comparing the declared return type of the function supplied to apply_along_axis with the actual. The two overloaded function definitions are given below.

Overload 1:

def [_P`-1, _SCT: generic] apply_along_axis(func1d: Callable[[ndarray[Any, dtype[Any]], **_P], _SupportsArray[dtype[_SCT]] | _NestedSequence[_SupportsArray[dtype[_SCT]]]], axis: SupportsIndex, arr: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], *args: _P.args, **kwargs: _P.kwargs) -> ndarray[Any, dtype[_SCT]]

Overload 2:

def [_P`-1] apply_along_axis(func1d: Callable[[ndarray[Any, dtype[Any]], **_P], Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]], axis: SupportsIndex, arr: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], *args: _P.args, **kwargs: _P.kwargs) -> ndarray[Any, dtype[Any]]

Notice that the return types don’t include None, which is understandable because None can be substituted for every type in Python. mypy, however, doesn’t seem to understand this.