Skip to content
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

Misleading warning message when using ABCMeta instead of Protocol #14825

Open
noamraph opened this issue Mar 3, 2023 · 2 comments
Open

Misleading warning message when using ABCMeta instead of Protocol #14825

noamraph opened this issue Mar 3, 2023 · 2 comments
Labels
bug mypy got something wrong

Comments

@noamraph
Copy link

noamraph commented Mar 3, 2023

Bug Report

To Reproduce

I run this code, expecting it to type-check:

https://mypy-play.net/?mypy=latest&python=3.11&gist=d4c1190de2b786aea78cc801ee2e7829

Expected Behavior

I would expect an error perhaps like this:

main.py:42: error: Argument "key" to "max_if" has incompatible type "Callable[[str], Optional[int]]"; expected "Callable[[str], Optional[S]]" where S is a subclass of "Comparable"  [arg-type]

Actual Behavior

main.py:42: error: Argument "key" to "max_if" has incompatible type "Callable[[str], Optional[int]]"; expected "Callable[[str], None]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Note

This was very confusing. While investigating this bug, I found out that I had to define Comparable using typing.Protocol instead of ABCMeta, and then I got the code to type-check.

I guess that since mypy tries to find S which is Comparable, so that Optional[int] would match S | None, and since it recognizes that int isn't a subclass of Comparable, it is left with the None and this is what it shows in the error message. However, this is very confusing.

Your Environment

  • Mypy version used: 1.0.0
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: 3.11
@noamraph noamraph added the bug mypy got something wrong label Mar 3, 2023
@tmke8
Copy link
Contributor

tmke8 commented Mar 3, 2023

The function isn't really generic in S right? S only appears once.

If you write it like this instead:

def max_if(iterable: Iterable[T], *, key: Callable[[T], Comparable | None]) -> T | None:
    ...

then you get a relatively clear error message:

main.py:42: error: Argument "key" to "max_if" has incompatible type "Callable[[str], Optional[int]]"; expected "Callable[[str], Optional[Comparable]]"  [arg-type]

@noamraph
Copy link
Author

noamraph commented Mar 4, 2023

@tmke8 I believe that actually the function is generic in S. In my definition, Comparable means that a type is comparable to itself. The key function should return values from a specific type, which is comparable to itself. With the definition you suggested, the key function may return many different types, say int for one item and str for another.

(But even if the definition without another generic is correct, I would still want the error message to be more correct and more helpful.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

2 participants