Skip to content

Commit 86a868f

Browse files
committed
bpo-46475: Add typing.Never and typing.assert_never
1 parent d1beb24 commit 86a868f

File tree

4 files changed

+149
-5
lines changed

4 files changed

+149
-5
lines changed

Doc/library/typing.rst

+53
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,32 @@ These can be used as types in annotations and do not support ``[]``.
572572
* Every type is compatible with :data:`Any`.
573573
* :data:`Any` is compatible with every type.
574574

575+
.. data:: Never
576+
577+
Notation for the `bottom type <https://en.wikipedia.org/wiki/Bottom_type>`_,
578+
a type that has no members.
579+
580+
This can be used to define a function that should never be
581+
called, or a function that never returns::
582+
583+
from typing import Never
584+
585+
def never_call_me(arg: Never) -> None:
586+
pass
587+
588+
never_call_me(1) # type checker error
589+
590+
def stop() -> Never:
591+
return 1 # type checker error
592+
593+
The :func:`assert_never()` function uses the ``Never`` type to statically
594+
assert that code is unreachable.
595+
596+
.. versionadded:: 3.11
597+
598+
On older Python versions, :data:`NoReturn` may be used to express the
599+
same concept. ``Never`` was added to make the intended meaning more explicit.
600+
575601
.. data:: NoReturn
576602

577603
Special type indicating that a function never returns.
@@ -582,6 +608,11 @@ These can be used as types in annotations and do not support ``[]``.
582608
def stop() -> NoReturn:
583609
raise RuntimeError('no way')
584610

611+
``NoReturn`` can also be used as a general
612+
`bottom type <https://en.wikipedia.org/wiki/Bottom_type>`_, a type that
613+
has no values. The :data:`Never` type provides a more explicit name for
614+
the same concept. Type checkers should treat the two equivalently.
615+
585616
.. versionadded:: 3.5.4
586617
.. versionadded:: 3.6.2
587618

@@ -1932,6 +1963,28 @@ Functions and decorators
19321963
runtime we intentionally don't check anything (we want this
19331964
to be as fast as possible).
19341965

1966+
.. function:: assert_never(arg, /)
1967+
1968+
Statically assert that a line of code is unreachable.
1969+
1970+
Example::
1971+
1972+
def int_or_str(arg: int | str) -> None:
1973+
match arg:
1974+
case int():
1975+
print("It's an int")
1976+
case str():
1977+
print("It's a str")
1978+
case _:
1979+
assert_never(arg)
1980+
1981+
If a type checker finds that a call to ``assert_never()`` is
1982+
reachable, it will emit an error.
1983+
1984+
At runtime, this throws an exception when called.
1985+
1986+
.. versionadded:: 3.11
1987+
19351988
.. decorator:: overload
19361989

19371990
The ``@overload`` decorator allows describing functions and methods

Lib/test/test_typing.py

+41-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from unittest import TestCase, main, skipUnless, skip
1010
from copy import copy, deepcopy
1111

12-
from typing import Any, NoReturn
12+
from typing import Any, NoReturn, Never, assert_never
1313
from typing import TypeVar, AnyStr
1414
from typing import T, KT, VT # Not in __all__.
1515
from typing import Union, Optional, Literal
@@ -156,6 +156,46 @@ def test_cannot_instantiate(self):
156156
type(NoReturn)()
157157

158158

159+
class NeverTests(BaseTestCase):
160+
161+
def test_never_instance_type_error(self):
162+
with self.assertRaises(TypeError):
163+
isinstance(42, Never)
164+
165+
def test_never_subclass_type_error(self):
166+
with self.assertRaises(TypeError):
167+
issubclass(Employee, Never)
168+
with self.assertRaises(TypeError):
169+
issubclass(Never, Employee)
170+
171+
def test_repr(self):
172+
self.assertEqual(repr(Never), 'typing.Never')
173+
174+
def test_not_generic(self):
175+
with self.assertRaises(TypeError):
176+
Never[int]
177+
178+
def test_cannot_subclass(self):
179+
with self.assertRaises(TypeError):
180+
class A(Never):
181+
pass
182+
with self.assertRaises(TypeError):
183+
class A(type(Never)):
184+
pass
185+
186+
def test_cannot_instantiate(self):
187+
with self.assertRaises(TypeError):
188+
Never()
189+
with self.assertRaises(TypeError):
190+
type(Never)()
191+
192+
193+
class AssertNeverTests(BaseTestCase):
194+
def test_exception(self):
195+
with self.assertRaises(RuntimeError):
196+
assert_never(None)
197+
198+
159199
class TypeVarTests(BaseTestCase):
160200

161201
def test_basic_plain(self):

Lib/typing.py

+53-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Imports and exports, all public names should be explicitly added to __all__.
66
* Internal helper functions: these should never be used in code outside this module.
77
* _SpecialForm and its instances (special forms):
8-
Any, NoReturn, ClassVar, Union, Optional, Concatenate
8+
Any, NoReturn, Never, ClassVar, Union, Optional, Concatenate
99
* Classes whose instances can be type arguments in addition to types:
1010
ForwardRef, TypeVar and ParamSpec
1111
* The core of internal generics API: _GenericAlias and _VariadicGenericAlias, the latter is
@@ -117,12 +117,14 @@ def _idfunc(_, x):
117117

118118
# One-off things.
119119
'AnyStr',
120+
'assert_never',
120121
'cast',
121122
'final',
122123
'get_args',
123124
'get_origin',
124125
'get_type_hints',
125126
'is_typeddict',
127+
'Never',
126128
'NewType',
127129
'no_type_check',
128130
'no_type_check_decorator',
@@ -173,7 +175,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, is_class=False):
173175
if (isinstance(arg, _GenericAlias) and
174176
arg.__origin__ in invalid_generic_forms):
175177
raise TypeError(f"{arg} is not valid as type argument")
176-
if arg in (Any, NoReturn, Final):
178+
if arg in (Any, NoReturn, Never, Final):
177179
return arg
178180
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
179181
raise TypeError(f"Plain {arg} is not valid as type argument")
@@ -441,8 +443,32 @@ def NoReturn(self, parameters):
441443
def stop() -> NoReturn:
442444
raise Exception('no way')
443445
444-
This type is invalid in other positions, e.g., ``List[NoReturn]``
445-
will fail in static type checkers.
446+
Static type checkers treat this as a general bottom type,
447+
a type with no members. The typing.Never type provides a
448+
more explicit name for that concept.
449+
450+
"""
451+
raise TypeError(f"{self} is not subscriptable")
452+
453+
@_SpecialForm
454+
def Never(self, parameters):
455+
"""Notation for the bottom type, a type that has no members.
456+
457+
This can be used to define a function that should never be
458+
called, or a function that never returns::
459+
460+
from typing import Never
461+
462+
def never_call_me(arg: Never) -> None:
463+
pass
464+
465+
never_call_me(1) # type checker error
466+
467+
def stop() -> Never:
468+
return 1 # type checker error
469+
470+
The assert_never() function uses the Never type to statically
471+
assert that code is unreachable.
446472
"""
447473
raise TypeError(f"{self} is not subscriptable")
448474

@@ -1941,6 +1967,29 @@ class Film(TypedDict):
19411967
return isinstance(tp, _TypedDictMeta)
19421968

19431969

1970+
def assert_never(arg: Never, /) -> Never:
1971+
"""Statically assert that a line of code is unreachable.
1972+
1973+
Example::
1974+
1975+
def int_or_str(arg: int | str) -> None:
1976+
match arg:
1977+
case int():
1978+
print("It's an int")
1979+
case str():
1980+
print("It's a str")
1981+
case _:
1982+
assert_never(arg)
1983+
1984+
If a type checker finds that a call to assert_never() is
1985+
reachable, it will emit an error.
1986+
1987+
At runtime, this throws an exception when called.
1988+
1989+
"""
1990+
raise RuntimeError("Unreachable code")
1991+
1992+
19441993
def no_type_check(arg):
19451994
"""Decorator to indicate that annotations are not type hints.
19461995
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :data:`typing.Never` and :func:`typing.assert_never`. Patch by Jelle
2+
Zijlstra.

0 commit comments

Comments
 (0)