bpo-46475: Add typing.Never and typing.assert_never (GH-30842)

This commit is contained in:
Jelle Zijlstra 2022-02-08 10:50:26 -08:00 committed by GitHub
parent 1e6214dbd6
commit 243436f377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 151 additions and 19 deletions

View file

@ -5,7 +5,7 @@ At large scale, the structure of the module is following:
* Imports and exports, all public names should be explicitly added to __all__.
* Internal helper functions: these should never be used in code outside this module.
* _SpecialForm and its instances (special forms):
Any, NoReturn, ClassVar, Union, Optional, Concatenate
Any, NoReturn, Never, ClassVar, Union, Optional, Concatenate
* Classes whose instances can be type arguments in addition to types:
ForwardRef, TypeVar and ParamSpec
* The core of internal generics API: _GenericAlias and _VariadicGenericAlias, the latter is
@ -117,12 +117,14 @@ __all__ = [
# One-off things.
'AnyStr',
'assert_never',
'cast',
'final',
'get_args',
'get_origin',
'get_type_hints',
'is_typeddict',
'Never',
'NewType',
'no_type_check',
'no_type_check_decorator',
@ -175,7 +177,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
if (isinstance(arg, _GenericAlias) and
arg.__origin__ in invalid_generic_forms):
raise TypeError(f"{arg} is not valid as type argument")
if arg in (Any, NoReturn, Self, ClassVar, Final, TypeAlias):
if arg in (Any, NoReturn, Never, Self, ClassVar, Final, TypeAlias):
return arg
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
raise TypeError(f"Plain {arg} is not valid as type argument")
@ -441,8 +443,39 @@ def NoReturn(self, parameters):
def stop() -> NoReturn:
raise Exception('no way')
This type is invalid in other positions, e.g., ``List[NoReturn]``
will fail in static type checkers.
NoReturn can also be used as a bottom type, a type that
has no values. Starting in Python 3.11, the Never type should
be used for this concept instead. Type checkers should treat the two
equivalently.
"""
raise TypeError(f"{self} is not subscriptable")
# This is semantically identical to NoReturn, but it is implemented
# separately so that type checkers can distinguish between the two
# if they want.
@_SpecialForm
def Never(self, parameters):
"""The bottom type, a type that has no members.
This can be used to define a function that should never be
called, or a function that never returns::
from typing import Never
def never_call_me(arg: Never) -> None:
pass
def int_or_str(arg: int | str) -> None:
never_call_me(arg) # type checker error
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
never_call_me(arg) # ok, arg is of type Never
"""
raise TypeError(f"{self} is not subscriptable")
@ -2060,6 +2093,29 @@ def is_typeddict(tp):
return isinstance(tp, _TypedDictMeta)
def assert_never(arg: Never, /) -> Never:
"""Statically assert that a line of code is unreachable.
Example::
def int_or_str(arg: int | str) -> None:
match arg:
case int():
print("It's an int")
case str():
print("It's a str")
case _:
assert_never(arg)
If a type checker finds that a call to assert_never() is
reachable, it will emit an error.
At runtime, this throws an exception when called.
"""
raise AssertionError("Expected code to be unreachable")
def no_type_check(arg):
"""Decorator to indicate that annotations are not type hints.