python / mypy

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

Confusing message if callable is incompatible due to argument name #4530

Open JukkaL opened 6 years ago

JukkaL commented 6 years ago

Mypy generates a confusing error for this program:

from typing import Callable, Union
from mypy_extensions import Arg

def f(x: Callable[[Arg(int, 'x')], None]) -> None: pass

y: Callable[[Union[int, str]], None]
f(y)  # error

Here is the output:

t.py:7: error: Argument 1 to "f" has incompatible type "Callable[[Union[int, str]], None]"; expected "Callable[[int], None]"

The actual error is the missing argument name in the type of y, but the error message gives no hint about this.

JukkaL commented 6 years ago

Added high priority since this is very confusing and we just received another report about this.

ilevkivskyi commented 6 years ago

Another similar example with even more confusing error message was found by @msullivan:

from typing import Optional, List, Tuple, Dict, Callable

def foo(x: int) -> None: pass
def bar(x: int) -> None: pass

funcs = [foo, bar]
reveal_type(funcs)

def oops(x: List[Callable[[int], None]]) -> None:
    reveal_type(x)

oops(funcs)

produces the error message Argument 1 to "oops" has incompatible type "List[Callable[[int], None]]"; expected "List[Callable[[int], None]]", followed by some advice about covariance.

ilevkivskyi commented 6 years ago

We should probably schedule this one for next sprint usability bugs squashing week. This happened again, it can very challenging with more complex signatures.

Michael0x2a commented 6 years ago

We also run into similar issues with sufficiently complex overload signatures -- for example, I ended up being confused by this sort of thing in https://github.com/python/mypy/issues/5235.

FWIW, one solution I was sort of tossing around was to modify or refactor subtypes.is_callable_compatible so it returns the reason why two callables are incompatible, rather then just a bool. The code doing the checking could then either pass that info into the error handler, or the error handling logic could re-run that check to extract the reason.

ilevkivskyi commented 5 years ago

Note that the error can be even more cryptic if type inference fails because of name mismatch, see https://github.com/python/mypy/issues/6928 for example.