Description
Bug Report
Due to duck typing handling type narrowing within a function accepting both int and float (even explicitly) doesn't work correctly.
To Reproduce
Consider this code, that actually leads to a type error
from typing import NewType
Euro = NewType("Euro", float)
def to_eur(user_in: int | float | str) -> Euro:
reveal_type(user_in) # int | float | str as expected
if isinstance(user_in, float):
reveal_type(user_in) # float
return Euro(user_in)
reveal_type(user_in) # str, but expected str | int
converted = float(user_in.rstrip("€"))
return Euro(converted)
print(to_eur(10)) # Raises type error as int has no method rstrip
Expected Behavior
I would expect that MyPy would warn me and that the expected reveal_type after the the isinstance call is int | float
This should be even the case, if int
is not explicitly given.
Consider the modification of above:
from typing import NewType
Euro = NewType("Euro", float)
def to_eur(user_in: float | str) -> Euro:
reveal_type(user_in) # float | str as expected
if isinstance(user_in, float):
reveal_type(user_in) # float
return Euro(user_in)
reveal_type(user_in) # str
converted = float(user_in.rstrip("€"))
return Euro(converted)
print(to_eur(10)) # Raises type error as int has no method rstrip
would still raise a runtime type error and no typing error at all.
Also handling all duck types explicitly as unions when type narrowing would probably handle the non reachable bugs described in
#12643.
This is also suggested in #11516
Actual Behavior
No error / warning raised by MyPy
Your Environment
See MyPy Playground
- Mypy version used: 0.950
- Mypy command-line flags: no
- Python version used: 3.10
- Operating system and version: Fedora/Online
Maybe related issues:
#11511