Skip to content

Misleading error message when using ParamSpec with higher-order decorators and async functions #18493

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

Closed
wesleywright opened this issue Jan 20, 2025 · 1 comment · Fixed by #18495
Labels
bug mypy got something wrong topic-error-reporting How we report errors

Comments

@wesleywright
Copy link
Collaborator

wesleywright commented Jan 20, 2025

Bug Report

There seems to be a niche edge case where mypy detects a mismatch between parameter names for a ParamSpec but doesn't actually report the difference in parameter names. In my reproducible example, it actually prints a confusing error about the return types, even though the return types actually unify just fine. I've only been able to reproduce it with an async function, but I'm not sure if it's limited to async functions.

This is probably a relatively rare issue for users to hit because it involves a higher-order function that returns a decorator, ParamSpec, and async — all of which are somewhat niche on their own. However, the error output still seems very misleading to me.

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.12&gist=12a1a49ced24294d0d1395f9317afcc1

from typing import Any, Awaitable, Callable, ParamSpec, TypeVar, Iterator

P = ParamSpec("P")
R = TypeVar("R")


def decorator2(f: Callable[P, None]) -> Callable[
    [Callable[P, Awaitable[None]]],
    Callable[P, Awaitable[None]],
]:
    return lambda f: f


def key2(x: int) -> None:
    ...


@decorator2(key2)
async def foo2(y: int) -> None:
    ...

Expected Behavior

Mypy should print an error pointing out that key2 takes a parameter named x while foo2 takes a parameter named y. For example, if you rewrote this example so that the foo2 is non-async and the decorator doesn't use Awaitable, you would get this error message:

error: Argument 1 has incompatible type "Callable[[Arg(int, 'y')], None]"; expected "Callable[[Arg(int, 'x')], None]"  [arg-type]

Actual Behavior

Mypy gives an error that does not describe the difference in argument names, but which does describe a difference in return types (even though these return types actually unify just fine!):

error: Argument 1 has incompatible type "Callable[[int], Coroutine[Any, Any, None]]"; expected "Callable[[int], Awaitable[None]]"  [arg-type]

Your Environment

Discovered while testing latest master to prepare the 1.15 release, but this behavior seems to be longstanding; I see the same error message when using the code from mypy v1.0.0. It seems to reproduce out of the box in Mypy, as demonstrated by the vanilla mypy playground link above.

@wesleywright wesleywright added the bug mypy got something wrong label Jan 20, 2025
@wesleywright wesleywright changed the title Misleading error message when using ParamSpec with higher-order decorators Misleading error message when using ParamSpec with higher-order decorators and async functions Jan 20, 2025
@sterliakov sterliakov added the topic-error-reporting How we report errors label Jan 20, 2025
@sterliakov
Copy link
Collaborator

Rewriting the decorator as

def decorator2(f: Callable[P, None]) -> Callable[
    [Callable[P, Coroutine[Any, Any, None]]],
    Callable[P, Coroutine[Any, Any, None]],
]: ...

produces a more comprehensible message:

Argument 1 has incompatible type "Callable[[Arg(int, 'y')], Coroutine[Any, Any, None]]"; expected "Callable[[Arg(int, 'x')], Coroutine[Any, Any, None]]"  [arg-type]

So this is likely a problem of some internal representation/normalization that confuses error message formatter?

x612skm pushed a commit to x612skm/mypy-dev that referenced this issue Feb 24, 2025
…rn types in error messages (python#18495)

Fixes python#18493.

Improves message in python#12013 and python#4530, but probably still doesn't make it
clear enough.

Use higher verbosity for type formatting in error message if callables'
return types are compatible and supertype has some named arguments, as
that is a popular source of confusion.
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-error-reporting How we report errors
Projects
None yet
3 participants
@wesleywright @sterliakov and others