typeshed
typeshed copied to clipboard
NotImplementedType behaves incorrectly in unions with classes
from collections.abc import Callable
from types import NotImplementedType
class MyClass:
pass
callable_union: Callable | NotImplementedType
reveal_type(callable_union)
if not isinstance(callable_union, NotImplementedType):
reveal_type(callable_union) # Gets narrowed correctly
class_union: MyClass | NotImplementedType
reveal_type(class_union)
if not isinstance(class_union, NotImplementedType):
reveal_type(class_union) # Mypy does not narrow the type
Leads to the following output:
test.py:10: note: Revealed type is "Union[def (*Any, **Any) -> Any, builtins._NotImplementedType]"
test.py:12: note: Revealed type is "def (*Any, **Any) -> Any"
test.py:15: note: Revealed type is "Union[test.MyClass, builtins._NotImplementedType]"
test.py:17: note: Revealed type is "Union[test.MyClass, builtins._NotImplementedType]"
_NotImplementedType
in stdlib/builtins.pyi
currently inherits from Any
. Removing that inheritance as in the below example fixes the issue for me:
from typing import final
@final
class _NotImplementedType:
# A little weird, but typing the __call__ as NotImplemented makes the error message
# for NotImplemented() much better
__call__: NotImplemented # type: ignore[valid-type] # pyright: ignore[reportGeneralTypeIssues]
fixed_type: MyClass | _NotImplementedType
reveal_type(fixed_type)
if not isinstance(fixed_type, _NotImplementedType):
reveal_type(fixed_type) # Gets narrowed correctly
Edit: The inheritance from Any
is actually by design: https://github.com/python/mypy/issues/4791#issuecomment-1694810031