diff --git a/conformance/tests/narrowing_typeguard.py b/conformance/tests/narrowing_typeguard.py index 193f9b93..1f76ad71 100644 --- a/conformance/tests/narrowing_typeguard.py +++ b/conformance/tests/narrowing_typeguard.py @@ -1,10 +1,10 @@ """ -Tests TypeGuard functionality. +Tests TypeGuard functionality, including async support. """ # Specification: https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeguard -from typing import Any, Callable, Protocol, Self, TypeGuard, TypeVar, assert_type +from typing import Any, Callable, Protocol, Self, TypeGuard, TypeVar, assert_type, Awaitable T = TypeVar("T") @@ -165,3 +165,16 @@ def bool_typeguard(val: object) -> TypeGuard[bool]: takes_int_typeguard(int_typeguard) # OK takes_int_typeguard(bool_typeguard) # OK + +# -------------------- ASYNC TYPEGUARD SUPPORT -------------------- + +async def async_typeguard_test(val: object) -> TypeGuard[int]: + return isinstance(val, int) + +async def test_async_typeguard(): + val: int | str = 10 + if await async_typeguard_test(val): # Ensure narrowing works here + assert_type(val, int) + else: + assert_type(val, str) + diff --git a/conformance/tests/narrowing_typeis.py b/conformance/tests/narrowing_typeis.py index 8632e67a..e5d2d37e 100644 --- a/conformance/tests/narrowing_typeis.py +++ b/conformance/tests/narrowing_typeis.py @@ -1,5 +1,5 @@ """ -Tests TypeIs functionality. +Tests TypeIs functionality, including async support. """ # Specification: https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeis @@ -20,7 +20,6 @@ def func1(names: tuple[str, ...]): else: assert_type(names, tuple[str, ...]) - # > The final narrowed type may be narrower than **R**, due to the constraints of the # > argument's previously-known type @@ -37,7 +36,6 @@ async def func2(val: int | Awaitable[int]): else: assert_type(val, int) - T_A = TypeVar("T_A", bound="A") class A: @@ -65,7 +63,6 @@ class B(A): # > passed to the function. The function may accept additional arguments, # > but they are not affected by type narrowing. - def func3() -> None: val1 = object() if A().tg_1(val1): @@ -95,7 +92,6 @@ def func3() -> None: if B().tg4(val7): assert_type(val7, B) - # > If a type narrowing function # > is implemented as an instance method or class method, the first positional # > argument maps to the second parameter (after self or cls). @@ -115,39 +111,30 @@ def tg_2(cls) -> TypeIs[int]: # E # > contexts, it is treated as a subtype of bool. For example, ``Callable[..., TypeIs[int]]`` # > is assignable to ``Callable[..., bool]``. - def takes_callable_bool(f: Callable[[object], bool]) -> None: pass - def takes_callable_str(f: Callable[[object], str]) -> None: pass - def simple_typeguard(val: object) -> TypeIs[int]: return isinstance(val, int) - takes_callable_bool(simple_typeguard) # OK takes_callable_str(simple_typeguard) # E - class CallableBoolProto(Protocol): def __call__(self, val: object) -> bool: ... - class CallableStrProto(Protocol): def __call__(self, val: object) -> str: ... - def takes_callable_bool_proto(f: CallableBoolProto) -> None: pass - def takes_callable_str_proto(f: CallableStrProto) -> None: pass - takes_callable_bool_proto(simple_typeguard) # OK takes_callable_str_proto(simple_typeguard) # E @@ -170,7 +157,6 @@ def is_int_typeguard(val: object) -> TypeGuard[int]: takes_typeis(is_int_typeguard) # E takes_typeis(is_int_typeis) # OK - # > Unlike ``TypeGuard``, ``TypeIs`` is invariant in its argument type: # > ``TypeIs[B]`` is not a subtype of ``TypeIs[A]``, # > even if ``B`` is a subtype of ``A``. @@ -178,15 +164,12 @@ def is_int_typeguard(val: object) -> TypeGuard[int]: def takes_int_typeis(f: Callable[[object], TypeIs[int]]) -> None: pass - def int_typeis(val: object) -> TypeIs[int]: return isinstance(val, int) - def bool_typeis(val: object) -> TypeIs[bool]: return isinstance(val, bool) - takes_int_typeis(int_typeis) # OK takes_int_typeis(bool_typeis) # E @@ -195,6 +178,18 @@ def bool_typeis(val: object) -> TypeIs[bool]: def bad_typeis(x: int) -> TypeIs[str]: # E return isinstance(x, str) - def bad_typeis_variance(x: list[object]) -> TypeIs[list[int]]: # E return all(isinstance(x, int) for x in x) + +# -------------------- ASYNC TYPEIS SUPPORT -------------------- + +async def async_typeis_test(val: object) -> TypeIs[int]: + return isinstance(val, int) + +async def test_async_typeis(): + val: int | str = 10 + if await async_typeis_test(val): # Ensure narrowing works here + assert_type(val, int) + else: + assert_type(val, str) +