From 54d645ee95e9220745cd65160fb720aedf5cb9b6 Mon Sep 17 00:00:00 2001 From: DetachHead Date: Sat, 8 Jan 2022 03:33:48 +1000 Subject: [PATCH] add `issubform` support for `Never` and ban instanciating it at runtime --- basedtyping/__init__.py | 18 ++++++++++++++---- tests/test_never_type/test_runtime.py | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/basedtyping/__init__.py b/basedtyping/__init__.py index 2891017..c68e5d5 100644 --- a/basedtyping/__init__.py +++ b/basedtyping/__init__.py @@ -11,6 +11,7 @@ Sequence, TypeVar, _GenericAlias, + _SpecialForm, cast, ) @@ -26,6 +27,7 @@ """ Never = NoReturn + """a type that can never exist. this is the narrowest possible type""" else: # for isinstance checks Function = Callable @@ -34,6 +36,9 @@ class _NeverMetaclass(type): def __instancecheck__(cls, instance: object) -> bool: return False + def __call__(cls) -> Never: + raise TypeError("cannot instanciate Never") + class Never(metaclass=_NeverMetaclass): pass @@ -223,8 +228,10 @@ def __class_getitem__(cls, item) -> type[ReifiedGeneric[T]]: # TODO: make this work with any "form", not just unions # should be (form: TypeForm, forminfo: TypeForm) -def issubform(form: type | UnionType, forminfo: type | UnionType) -> bool: - """EXPERIMENTAL: Warning, this function currently only supports unions. +def issubform( + form: type | UnionType | _SpecialForm, forminfo: type | UnionType | _SpecialForm +) -> bool: + """EXPERIMENTAL: Warning, this function currently only supports unions and ``Never``. Returns ``True`` if ``form`` is a subform (specialform or subclass) of ``forminfo``. @@ -241,9 +248,12 @@ def issubform(form: type | UnionType, forminfo: type | UnionType) -> bool: >>> issubform(int | str, object) True """ + # type ignores because issubclass doesn't support _SpecialForm but we do if isinstance(form, UnionType | OldUnionType): for t in cast(Sequence[type], cast(UnionType, form).__args__): - if not issubclass(t, forminfo): + if not issubform(t, forminfo): return False return True - return issubclass(form, forminfo) + if form is Never: + return True + return issubclass(form, forminfo) # type:ignore[arg-type] diff --git a/tests/test_never_type/test_runtime.py b/tests/test_never_type/test_runtime.py index 224d135..43ac3ac 100644 --- a/tests/test_never_type/test_runtime.py +++ b/tests/test_never_type/test_runtime.py @@ -1,4 +1,6 @@ -from basedtyping import Never +from pytest import raises + +from basedtyping import Never, issubform def test_isinstance() -> None: @@ -7,3 +9,16 @@ def test_isinstance() -> None: # https://github.com/KotlinIsland/basedmypy/issues/136 Never, # type:ignore[arg-type] ) + + +def test_issubform_true() -> None: + assert issubform(Never, int) + + +def test_issubform_false() -> None: + assert not issubform(str, Never) + + +def test_cant_instanciate() -> None: + with raises(TypeError): + Never() # type:ignore[operator]