Skip to content

Commit

Permalink
bpo-46480: add typing.assert_type (GH-30843)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Co-authored-by: David Foster <david@dafoster.net>
  • Loading branch information
3 people authored Mar 17, 2022
1 parent 7c353b7 commit 96568e9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 1 deletion.
25 changes: 25 additions & 0 deletions Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 17 additions & 1 deletion Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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
Expand Down Expand Up @@ -3302,6 +3302,22 @@ def test_errors(self):
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

Expand Down
17 changes: 17 additions & 0 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def _idfunc(_, x):

# One-off things.
'AnyStr',
'assert_type',
'assert_never',
'cast',
'final',
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :func:`typing.assert_type`. Patch by Jelle Zijlstra.

0 comments on commit 96568e9

Please sign in to comment.