Type inference for `max()` "tainted" by `default`
Consider the following code:
from __future__ import annotations
from collections.abc import Iterable
from packaging.version import Version
def best_str(strs: Iterable[str]) -> str | None:
return max(strs, key=keyfunc, default=None)
def keyfunc(s: str) -> str:
return s[::-1]
I believe this should type-check without issue; in particular, by my reading of max()'s type annotations in typeshed, the key callable should be expected to take only those types contained in the iterable. However, mypy disagrees:
max-arg2.py:6: error: Argument "key" to "max" has incompatible type "Callable[[str], str]"; expected "Callable[[str | None], SupportsDunderLT[Any] | SupportsDunderGT[Any]]" [arg-type]
Note that mypy expects the key callable to accept str | None even though strs is an Iterable[str]. Moreover, if the default argument is omitted and the return type of best_str() is changed to str, then mypy accepts the code.
Your Environment
- Mypy version used: 1.6.0
- Mypy command-line flags: None
- Mypy configuration options from
mypy.ini(and other config files): None - Python version used: 3.11.6
Thanks for the great bug report! It looks like this still repros if you use --new-type-inference, as well.
default is a red herring. This has nothing to do with any fancy inference logic, it is plain old overusing of outer context. I bet if you will rewrite it as temp = max(...); return temp it will work. A while ago I added a special-casing for this for assignments, see https://github.com/python/mypy/pull/14151, I guess we may want to have matching special-casing for return.
@ilevkivskyi confirmed assigning to a local variable first makes this work.