python / mypy

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

Unexpected behavior with type narrowing and `Callable` #16738

Open rouge8 opened 8 months ago

rouge8 commented 8 months ago

Bug Report

I can't really describe the bug well, but hopefully the gist below illustrates it. I'm attempting to go from a list of Callables that includes types (e.g. int, float, etc.) and narrow that to a list of types.

To Reproduce

https://mypy-play.net/?mypy=1.8.0&python=3.11&gist=05a1003c1414446f7196ec4c7c57ded7

Expected Behavior

The gist has no type errors.

Actual Behavior

main.py:5: error: List comprehension has incompatible type List[Callable[..., SupportsComplex]]; expected List[type]  [misc]
Found 1 error in 1 file (checked 1 source file)
Hnasar commented 7 months ago

It seems like type is (incorrectly) not treated as a subtype of Callable, so the isinstance checks are not working at typechecking time.

(Perhaps related to #1831 #11486 #11478, #11469, #11470 )

Here's a smaller failing example (and a workaround).

# foo.py
from typing import *

# workaround: if defined like this it correctly catches the assert failure
# thing: Callable[..., int] | type[int] = int

thing: Callable[..., int] = int

if isinstance(thing, type):
    assert_never(thing)

(playground)

# passes typechecking
$ py -m mypy --strict foo.py 
Success: no issues found in 1 source file
# fails at runtime
$ foo.py 
Traceback (most recent call last):
  File "/home/hashem/sources/mypy/foo.py", line 9, in <module>
    assert_never(thing)
  File "/usr/lib64/python3.12/typing.py", line 2374, in assert_never
    raise AssertionError(f"Expected code to be unreachable, but got: {value}")
AssertionError: Expected code to be unreachable, but got: <class 'int'>