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

MyPy forgets callable type guard in lambda in return statement of function with hinted return type #15862

Closed
lukas-lang opened this issue Aug 13, 2023 · 2 comments
Labels
bug mypy got something wrong

Comments

@lukas-lang
Copy link

Bug Report

There seems to be some strange interaction between hinted return types, lambdas and the callable type guard.

To Reproduce
Here is a minimal example, together with some related examples that are not broken, showing that the return type, lambda and callable are all required for this to break:

(Playground: https://mypy-play.net/?mypy=latest&python=3.11&gist=26f7a80bf3eff44ebf560ce5db694107)

from typing import Callable

def foo(x: int | Callable) -> int | Callable:
    if callable(x):
        return lambda: x()
    else:
        return x


def foo_fixed(x: int | Callable) -> int | Callable:
    if callable(x):
        ret = lambda: x()
        return ret
    else:
        return x


def foo_fixed2(x: int | Callable):
    if callable(x):
        return lambda: x()
    else:
        return x


def foo_fixed3(x: int | float) -> int | Callable[[], float]:
    if isinstance(x, float):
        return lambda: x
    else:
        return x


def foo_fixed4(x: int | Callable) -> float | Callable:
    if callable(x):
        return x
    else:
        return x + 1.0

Expected Behavior
Mypy should remember that x is a Callable within the lambda in the return statement.

Actual Behavior

MyPy forgets the type guard, and thinks that x is int | Callable in line 5:

main.py:5: error: "int" not callable  [operator]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.5.0
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: 3.11
@lukas-lang lukas-lang added the bug mypy got something wrong label Aug 13, 2023
@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Aug 13, 2023

Thanks for the issue!

The underlying reason is that closures in Python are late binding, see https://mypy.readthedocs.io/en/stable/common_issues.html#narrowing-and-inner-functions.

mypy recently improved heuristics here (see #2608), but I guess not quite well enough to avoid the false positive in your case.

@hauntsaninja
Copy link
Collaborator

Fixed by #16407

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