Closed
Description
Bug Report
Variables whose type is a union that includes types.EllipsisType can't be narrowed using if foo is Ellipsis
. That's unexpected to me, because Ellipsis is documented as the only instance of EllipsisType.
Similar but slightly different issues occur with NoneType and NotImplementedType.
To Reproduce
Minimal reproducing case:
from types import EllipsisType
def working(foo: EllipsisType | int) -> int:
if isinstance(foo, EllipsisType):
# Revealed type is "builtins.ellipsis" (seems to understand the singleton relationship here)
foo = 1
reveal_type(foo) # Revealed type is "builtins.int"
return foo # no error
def not_working(foo: EllipsisType | int) -> int:
if foo is Ellipsis:
reveal_type(foo) # Revealed type is "Union[builtins.ellipsis, builtins.int]" (why is int still here?)
foo = 1
reveal_type(foo) # Revealed type is "Union[builtins.ellipsis, builtins.int]"
return foo # error: Incompatible return value type (got "Union[ellipsis, int]", expected "int")
Mypy appears to believe that there might be values whose type is EllipsisType but which don't have the same identity as Ellipsis. I don't think that's possible.
Similar things happen with None and NoneType:
from types import NoneType
def working(foo: NoneType | int) -> int:
if isinstance(foo, NoneType):
reveal_type(foo) # Revealed type is "types.NoneType" (interesting difference from Ellipsis)
foo = 1
reveal_type(foo) # Revealed type is "builtins.int"
return foo # no error
def not_working(foo: NoneType | int) -> int:
if foo is None:
reveal_type(foo) # Doesn't show any output (?)
foo = 1
reveal_type(foo) # Revealed type is "Union[types.NoneType, builtins.int]"
return foo # Incompatible return value type (got "Union[NoneType, int]", expected "int")
NotImplemented and NotImplementedType pass without error, but appear to do so for the wrong reasons:
from types import NotImplementedType
def working(foo: NotImplementedType | int) -> int:
if isinstance(foo, NotImplementedType):
reveal_type(foo) # Revealed type is "builtins._NotImplementedType"
foo = 1
reveal_type(foo) # Revealed type is "Union[builtins._NotImplementedType, builtins.int]"
return foo # no error ??
def also_working(foo: NotImplementedType | int) -> int:
if foo is NotImplemented:
reveal_type(foo) # Revealed type is "Union[builtins._NotImplementedType, builtins.int]"
foo = 1
reveal_type(foo) # Revealed type is "Union[builtins._NotImplementedType, builtins.int]"
return foo # no error ??
Your Environment
- Mypy version used: mypy 0.961 (compiled: yes)
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini
(and other config files): none - Python version used: python Python 3.10.4
- Operating system and version: macOS 10.15.7