Skip to content

#1872 issue: Update narrowing_typeguar.py Update narrowing_typeis.py #1928

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions conformance/tests/narrowing_typeguard.py
Original file line number Diff line number Diff line change
@@ -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")
Expand Down Expand Up @@ -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)

33 changes: 14 additions & 19 deletions conformance/tests/narrowing_typeis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Tests TypeIs functionality.
Tests TypeIs functionality, including async support.
"""

# Specification: https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeis
Expand All @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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).
Expand All @@ -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

Expand All @@ -170,23 +157,19 @@ 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``.

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

Expand All @@ -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)