Skip to content

Commit 243436f

Browse files
bpo-46475: Add typing.Never and typing.assert_never (GH-30842)
1 parent 1e6214d commit 243436f

File tree

4 files changed

+151
-19
lines changed

4 files changed

+151
-19
lines changed

Doc/library/typing.rst

+56
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,34 @@ These can be used as types in annotations and do not support ``[]``.
574574
* Every type is compatible with :data:`Any`.
575575
* :data:`Any` is compatible with every type.
576576

577+
.. data:: Never
578+
579+
The `bottom type <https://en.wikipedia.org/wiki/Bottom_type>`_,
580+
a type that has no members.
581+
582+
This can be used to define a function that should never be
583+
called, or a function that never returns::
584+
585+
from typing import Never
586+
587+
def never_call_me(arg: Never) -> None:
588+
pass
589+
590+
def int_or_str(arg: int | str) -> None:
591+
never_call_me(arg) # type checker error
592+
match arg:
593+
case int():
594+
print("It's an int")
595+
case str():
596+
print("It's a str")
597+
case _:
598+
never_call_me(arg) # ok, arg is of type Never
599+
600+
.. versionadded:: 3.11
601+
602+
On older Python versions, :data:`NoReturn` may be used to express the
603+
same concept. ``Never`` was added to make the intended meaning more explicit.
604+
577605
.. data:: NoReturn
578606

579607
Special type indicating that a function never returns.
@@ -584,6 +612,12 @@ These can be used as types in annotations and do not support ``[]``.
584612
def stop() -> NoReturn:
585613
raise RuntimeError('no way')
586614

615+
``NoReturn`` can also be used as a
616+
`bottom type <https://en.wikipedia.org/wiki/Bottom_type>`_, a type that
617+
has no values. Starting in Python 3.11, the :data:`Never` type should
618+
be used for this concept instead. Type checkers should treat the two
619+
equivalently.
620+
587621
.. versionadded:: 3.5.4
588622
.. versionadded:: 3.6.2
589623

@@ -1979,6 +2013,28 @@ Functions and decorators
19792013
runtime we intentionally don't check anything (we want this
19802014
to be as fast as possible).
19812015

2016+
.. function:: assert_never(arg, /)
2017+
2018+
Assert to the type checker that a line of code is unreachable.
2019+
2020+
Example::
2021+
2022+
def int_or_str(arg: int | str) -> None:
2023+
match arg:
2024+
case int():
2025+
print("It's an int")
2026+
case str():
2027+
print("It's a str")
2028+
case _ as unreachable:
2029+
assert_never(unreachable)
2030+
2031+
If a type checker finds that a call to ``assert_never()`` is
2032+
reachable, it will emit an error.
2033+
2034+
At runtime, this throws an exception when called.
2035+
2036+
.. versionadded:: 3.11
2037+
19822038
.. function:: reveal_type(obj)
19832039

19842040
Reveal the inferred static type of an expression.

Lib/test/test_typing.py

+33-15
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
@@ -124,38 +124,56 @@ def test_any_works_with_alias(self):
124124
typing.IO[Any]
125125

126126

127-
class NoReturnTests(BaseTestCase):
127+
class BottomTypeTestsMixin:
128+
bottom_type: ClassVar[Any]
128129

129-
def test_noreturn_instance_type_error(self):
130+
def test_instance_type_error(self):
130131
with self.assertRaises(TypeError):
131-
isinstance(42, NoReturn)
132+
isinstance(42, self.bottom_type)
132133

133-
def test_noreturn_subclass_type_error(self):
134+
def test_subclass_type_error(self):
134135
with self.assertRaises(TypeError):
135-
issubclass(Employee, NoReturn)
136+
issubclass(Employee, self.bottom_type)
136137
with self.assertRaises(TypeError):
137-
issubclass(NoReturn, Employee)
138-
139-
def test_repr(self):
140-
self.assertEqual(repr(NoReturn), 'typing.NoReturn')
138+
issubclass(NoReturn, self.bottom_type)
141139

142140
def test_not_generic(self):
143141
with self.assertRaises(TypeError):
144-
NoReturn[int]
142+
self.bottom_type[int]
145143

146144
def test_cannot_subclass(self):
147145
with self.assertRaises(TypeError):
148-
class A(NoReturn):
146+
class A(self.bottom_type):
149147
pass
150148
with self.assertRaises(TypeError):
151-
class A(type(NoReturn)):
149+
class A(type(self.bottom_type)):
152150
pass
153151

154152
def test_cannot_instantiate(self):
155153
with self.assertRaises(TypeError):
156-
NoReturn()
154+
self.bottom_type()
157155
with self.assertRaises(TypeError):
158-
type(NoReturn)()
156+
type(self.bottom_type)()
157+
158+
159+
class NoReturnTests(BottomTypeTestsMixin, BaseTestCase):
160+
bottom_type = NoReturn
161+
162+
def test_repr(self):
163+
self.assertEqual(repr(NoReturn), 'typing.NoReturn')
164+
165+
166+
class NeverTests(BottomTypeTestsMixin, BaseTestCase):
167+
bottom_type = Never
168+
169+
def test_repr(self):
170+
self.assertEqual(repr(Never), 'typing.Never')
171+
172+
173+
class AssertNeverTests(BaseTestCase):
174+
def test_exception(self):
175+
with self.assertRaises(AssertionError):
176+
assert_never(None)
159177

160178

161179
class SelfTests(BaseTestCase):

Lib/typing.py

+60-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',
@@ -175,7 +177,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
175177
if (isinstance(arg, _GenericAlias) and
176178
arg.__origin__ in invalid_generic_forms):
177179
raise TypeError(f"{arg} is not valid as type argument")
178-
if arg in (Any, NoReturn, Self, ClassVar, Final, TypeAlias):
180+
if arg in (Any, NoReturn, Never, Self, ClassVar, Final, TypeAlias):
179181
return arg
180182
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
181183
raise TypeError(f"Plain {arg} is not valid as type argument")
@@ -441,8 +443,39 @@ 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+
NoReturn can also be used as a bottom type, a type that
447+
has no values. Starting in Python 3.11, the Never type should
448+
be used for this concept instead. Type checkers should treat the two
449+
equivalently.
450+
451+
"""
452+
raise TypeError(f"{self} is not subscriptable")
453+
454+
# This is semantically identical to NoReturn, but it is implemented
455+
# separately so that type checkers can distinguish between the two
456+
# if they want.
457+
@_SpecialForm
458+
def Never(self, parameters):
459+
"""The bottom type, a type that has no members.
460+
461+
This can be used to define a function that should never be
462+
called, or a function that never returns::
463+
464+
from typing import Never
465+
466+
def never_call_me(arg: Never) -> None:
467+
pass
468+
469+
def int_or_str(arg: int | str) -> None:
470+
never_call_me(arg) # type checker error
471+
match arg:
472+
case int():
473+
print("It's an int")
474+
case str():
475+
print("It's a str")
476+
case _:
477+
never_call_me(arg) # ok, arg is of type Never
478+
446479
"""
447480
raise TypeError(f"{self} is not subscriptable")
448481

@@ -2060,6 +2093,29 @@ class Film(TypedDict):
20602093
return isinstance(tp, _TypedDictMeta)
20612094

20622095

2096+
def assert_never(arg: Never, /) -> Never:
2097+
"""Statically assert that a line of code is unreachable.
2098+
2099+
Example::
2100+
2101+
def int_or_str(arg: int | str) -> None:
2102+
match arg:
2103+
case int():
2104+
print("It's an int")
2105+
case str():
2106+
print("It's a str")
2107+
case _:
2108+
assert_never(arg)
2109+
2110+
If a type checker finds that a call to assert_never() is
2111+
reachable, it will emit an error.
2112+
2113+
At runtime, this throws an exception when called.
2114+
2115+
"""
2116+
raise AssertionError("Expected code to be unreachable")
2117+
2118+
20632119
def no_type_check(arg):
20642120
"""Decorator to indicate that annotations are not type hints.
20652121
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)