Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add assert_type #1103

Merged
merged 4 commits into from Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions typing_extensions/CHANGELOG
@@ -1,3 +1,7 @@
# Unreleased

- Add `typing.assert_type`. Backport from bpo-46480.

# Release 4.1.1 (February 13, 2022)

- Fix importing `typing_extensions` on Python 3.7.0 and 3.7.1. Original
Expand Down
1 change: 1 addition & 0 deletions typing_extensions/README.rst
Expand Up @@ -45,6 +45,7 @@ This module currently contains the following:
- In ``typing`` since Python 3.11

- ``assert_never``
- ``assert_type``
- ``Never``
- ``reveal_type``
- ``Self`` (see PEP 673)
Expand Down
18 changes: 17 additions & 1 deletion typing_extensions/src/test_typing_extensions.py
Expand Up @@ -12,7 +12,7 @@
from unittest import TestCase, main, skipUnless, skipIf
from test import ann_module, ann_module2, ann_module3
import typing
from typing import TypeVar, Optional, Union, Any
from typing import TypeVar, Optional, Union, Any, AnyStr
from typing import T, KT, VT # Not in __all__.
from typing import Tuple, List, Dict, Iterable, Iterator, Callable
from typing import Generic, NamedTuple
Expand All @@ -23,6 +23,7 @@
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload, final, is_typeddict
from typing_extensions import TypeVarTuple, Unpack, dataclass_transform, reveal_type, Never, assert_never, LiteralString
from typing_extensions import assert_type
try:
from typing_extensions import get_type_hints
except ImportError:
Expand Down Expand Up @@ -445,6 +446,21 @@ def blah():
blah()


class AssertTypeTests(BaseTestCase):

def test_basics(self):
arg = 42
self.assertIs(assert_type(arg, int), arg)
self.assertIs(assert_type(arg, Union[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)


T_a = TypeVar('T_a')

Expand Down
21 changes: 21 additions & 0 deletions typing_extensions/src/typing_extensions.py
Expand Up @@ -1231,6 +1231,27 @@ class Film(TypedDict):
"""
return isinstance(tp, tuple(_TYPEDDICT_TYPES))


if hasattr(typing, "assert_type"):
assert_type = typing.assert_type

else:
def assert_type(__val, __typ):
"""Assert (to the type checker) that the value is of the given type.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More a critique of the original PR, but not sure that "Assert (to the type checker) that the value is of the given type." is the least confusing phrasing.

You're not telling the type checker a fact, you're asking it to confirm one, so maybe "Ask a static type checker to confirm that the value is of the given type".

Or at least, we should do more to distinguish it from assert isinstance(val, typ) (whose behaviour is what I would describe as "asserting something to the type checker")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you bring this up on cpython instead? The docstring here is just a copy of the CPython one. We can sync it back later if we want.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Should I open a PR directly against the original BPO or would you prefer I file its own BPO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep the same one


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


if hasattr(typing, "Required"):
get_type_hints = typing.get_type_hints
elif PEP_560:
Expand Down