bpo-46480: add typing.assert_type (GH-30843)

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: David Foster <david@dafoster.net>
This commit is contained in:
Jelle Zijlstra 2022-03-16 20:02:26 -07:00 committed by GitHub
parent 7c353b7594
commit 96568e995d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 1 deletions

View file

@ -2148,6 +2148,31 @@ Functions and decorators
runtime we intentionally don't check anything (we want this
to be as fast as possible).
.. function:: assert_type(val, typ, /)
Assert (to the type checker) that *val* has an inferred type of *typ*.
When the type checker encounters a call to ``assert_type()``, it
emits an error if the value is not of the specified type::
def greet(name: str) -> None:
assert_type(name, str) # OK, inferred type of `name` is `str`
assert_type(name, int) # type checker error
At runtime this returns the first argument unchanged with no side effects.
This function is useful for ensuring the type checker's understanding of a
script is in line with the developer's intentions::
def complex_function(arg: object):
# Do some complex type-narrowing logic,
# after which we hope the inferred type will be `int`
...
# Test whether the type checker correctly understands our function
assert_type(arg, int)
.. versionadded:: 3.11
.. function:: assert_never(arg, /)
Assert to the type checker that a line of code is unreachable.

View file

@ -16,7 +16,7 @@ from typing import Union, Optional, Literal
from typing import Tuple, List, Dict, MutableMapping
from typing import Callable
from typing import Generic, ClassVar, Final, final, Protocol
from typing import cast, runtime_checkable
from typing import assert_type, cast, runtime_checkable
from typing import get_type_hints
from typing import get_origin, get_args
from typing import is_typeddict
@ -3302,6 +3302,22 @@ class CastTests(BaseTestCase):
cast('hello', 42)
class AssertTypeTests(BaseTestCase):
def test_basics(self):
arg = 42
self.assertIs(assert_type(arg, int), arg)
self.assertIs(assert_type(arg, str | float), arg)
self.assertIs(assert_type(arg, AnyStr), arg)
self.assertIs(assert_type(arg, None), arg)
def test_errors(self):
# Bogus calls are not expected to fail.
arg = 42
self.assertIs(assert_type(arg, 42), arg)
self.assertIs(assert_type(arg, 'hello'), arg)
# We need this to make sure that `@no_type_check` respects `__module__` attr:
from test import ann_module8

View file

@ -118,6 +118,7 @@ __all__ = [
# One-off things.
'AnyStr',
'assert_type',
'assert_never',
'cast',
'final',
@ -2093,6 +2094,22 @@ def cast(typ, val):
return val
def assert_type(val, typ, /):
"""Assert (to the type checker) that the value is of the given type.
When the type checker encounters a call to assert_type(), it
emits an error if the value is not of the specified type::
def greet(name: str) -> None:
assert_type(name, str) # ok
assert_type(name, int) # type checker error
At runtime this returns the first argument unchanged and otherwise
does nothing.
"""
return val
_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
types.MethodType, types.ModuleType,
WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)

View file

@ -0,0 +1 @@
Add :func:`typing.assert_type`. Patch by Jelle Zijlstra.