From 2c720aba64ae69b45209350d07946eccda6af2f8 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 11 Jun 2022 16:54:19 +0100 Subject: [PATCH 1/5] `inspect`, `asyncio`: Use more `TypeGuard`s --- stdlib/asyncio/coroutines.pyi | 11 +++++--- stdlib/inspect.pyi | 47 ++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/stdlib/asyncio/coroutines.pyi b/stdlib/asyncio/coroutines.pyi index 6d4d507c6a4c..0bc4114f7370 100644 --- a/stdlib/asyncio/coroutines.pyi +++ b/stdlib/asyncio/coroutines.pyi @@ -1,8 +1,8 @@ import sys import types from collections.abc import Coroutine -from typing import Any -from typing_extensions import TypeGuard +from typing import Any, Callable, overload +from typing_extensions import ParamSpec, TypeGuard if sys.version_info >= (3, 11): __all__ = ("iscoroutinefunction", "iscoroutine") @@ -11,6 +11,8 @@ elif sys.version_info >= (3, 7): else: __all__ = ["coroutine", "iscoroutinefunction", "iscoroutine"] +_P = ParamSpec("_P") + if sys.version_info < (3, 11): from collections.abc import Callable from typing import TypeVar @@ -18,7 +20,10 @@ if sys.version_info < (3, 11): _F = TypeVar("_F", bound=Callable[..., Any]) def coroutine(func: _F) -> _F: ... -def iscoroutinefunction(func: object) -> bool: ... +@overload +def iscoroutinefunction(func: Callable[_P, Any]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... +@overload +def iscoroutinefunction(func: object) -> TypeGuard[Callable[..., Coroutine[Any, Any, Any]]]: ... if sys.version_info >= (3, 8): def iscoroutine(obj: object) -> TypeGuard[Coroutine[Any, Any, Any]]: ... diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index 38d928f43c9a..49a9ebc12a44 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -31,7 +31,7 @@ if sys.version_info >= (3, 7): MethodWrapperType, ) -from typing import Any, ClassVar, NamedTuple, Protocol, TypeVar, Union +from typing import Any, ClassVar, NamedTuple, Protocol, TypeVar, Union, overload from typing_extensions import Literal, ParamSpec, TypeGuard if sys.version_info >= (3, 11): @@ -182,22 +182,40 @@ def ismethod(object: object) -> TypeGuard[MethodType]: ... def isfunction(object: object) -> TypeGuard[FunctionType]: ... if sys.version_info >= (3, 8): - def isgeneratorfunction(obj: object) -> bool: ... - def iscoroutinefunction(obj: object) -> bool: ... + @overload + def isgeneratorfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... + @overload + def isgeneratorfunction(obj: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... + @overload + def iscoroutinefunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... + @overload + def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... else: - def isgeneratorfunction(object: object) -> bool: ... - def iscoroutinefunction(object: object) -> bool: ... + @overload + def isgeneratorfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... + @overload + def isgeneratorfunction(object: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... + @overload + def iscoroutinefunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... + @overload + def iscoroutinefunction(object: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... def isgenerator(object: object) -> TypeGuard[GeneratorType[Any, Any, Any]]: ... def iscoroutine(object: object) -> TypeGuard[CoroutineType[Any, Any, Any]]: ... def isawaitable(object: object) -> TypeGuard[Awaitable[Any]]: ... if sys.version_info >= (3, 8): - def isasyncgenfunction(obj: object) -> bool: ... + @overload + def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... + @overload + def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... else: - def isasyncgenfunction(object: object) -> bool: ... + @overload + def isasyncgenfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... + @overload + def isasyncgenfunction(object: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... class _SupportsSet(Protocol[_T_cont, _V_cont]): def __set__(self, __instance: _T_cont, __value: _V_cont) -> None: ... @@ -215,9 +233,7 @@ if sys.version_info >= (3, 11): def ismethodwrapper(object: object) -> TypeGuard[MethodWrapperType]: ... if sys.version_info >= (3, 7): - def isroutine( - object: object, - ) -> TypeGuard[ + _Routine: TypeAlias = ( FunctionType | LambdaType | MethodType @@ -226,14 +242,17 @@ if sys.version_info >= (3, 7): | WrapperDescriptorType | MethodDescriptorType | ClassMethodDescriptorType - ]: ... + ) +else: + _Routine: TypeAlias = FunctionType | LambdaType | MethodType | BuiltinFunctionType | BuiltinMethodType + +def isroutine(object: object) -> TypeGuard[_Routine]: ... + +if sys.version_info >= (3, 7): def ismethoddescriptor(object: object) -> TypeGuard[MethodDescriptorType]: ... def ismemberdescriptor(object: object) -> TypeGuard[MemberDescriptorType]: ... else: - def isroutine( - object: object, - ) -> TypeGuard[FunctionType | LambdaType | MethodType | BuiltinFunctionType | BuiltinMethodType]: ... def ismethoddescriptor(object: object) -> bool: ... def ismemberdescriptor(object: object) -> bool: ... From 0f74cd342f8cf90cf79ed76c6320611c36bad00a Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sat, 11 Jun 2022 17:01:19 +0100 Subject: [PATCH 2/5] Fix lint --- stdlib/asyncio/coroutines.pyi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/asyncio/coroutines.pyi b/stdlib/asyncio/coroutines.pyi index 0bc4114f7370..f67e5883316d 100644 --- a/stdlib/asyncio/coroutines.pyi +++ b/stdlib/asyncio/coroutines.pyi @@ -1,7 +1,7 @@ import sys import types -from collections.abc import Coroutine -from typing import Any, Callable, overload +from collections.abc import Callable, Coroutine +from typing import Any, overload from typing_extensions import ParamSpec, TypeGuard if sys.version_info >= (3, 11): @@ -14,7 +14,6 @@ else: _P = ParamSpec("_P") if sys.version_info < (3, 11): - from collections.abc import Callable from typing import TypeVar _F = TypeVar("_F", bound=Callable[..., Any]) From 14301c5d0c9332480ef3cb93dd61f1619ac3fb00 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sun, 12 Jun 2022 12:38:46 +0100 Subject: [PATCH 3/5] Experiment --- stdlib/asyncio/coroutines.pyi | 2 ++ stdlib/inspect.pyi | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/stdlib/asyncio/coroutines.pyi b/stdlib/asyncio/coroutines.pyi index f67e5883316d..7438f9bd816a 100644 --- a/stdlib/asyncio/coroutines.pyi +++ b/stdlib/asyncio/coroutines.pyi @@ -19,6 +19,8 @@ if sys.version_info < (3, 11): _F = TypeVar("_F", bound=Callable[..., Any]) def coroutine(func: _F) -> _F: ... +@overload +def iscoroutinefunction(func: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... @overload def iscoroutinefunction(func: Callable[_P, Any]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... @overload diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index 49a9ebc12a44..39753df09dac 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -4,7 +4,7 @@ import sys import types from _typeshed import Self from collections import OrderedDict -from collections.abc import Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet +from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Generator, Mapping, Sequence, Set as AbstractSet from types import ( AsyncGeneratorType, BuiltinFunctionType, @@ -182,21 +182,29 @@ def ismethod(object: object) -> TypeGuard[MethodType]: ... def isfunction(object: object) -> TypeGuard[FunctionType]: ... if sys.version_info >= (3, 8): + @overload + def isgeneratorfunction(obj: Callable[..., Generator[Any, Any, Any]]) -> bool: ... @overload def isgeneratorfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... @overload def isgeneratorfunction(obj: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... @overload + def iscoroutinefunction(obj: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... + @overload def iscoroutinefunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... @overload def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... else: + @overload + def isgeneratorfunction(object: Callable[..., Generator[Any, Any, Any]]) -> bool: ... @overload def isgeneratorfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, GeneratorType[Any, Any, Any]]]: ... @overload def isgeneratorfunction(object: object) -> TypeGuard[Callable[..., GeneratorType[Any, Any, Any]]]: ... @overload + def iscoroutinefunction(object: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... + @overload def iscoroutinefunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... @overload def iscoroutinefunction(object: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... @@ -206,12 +214,16 @@ def iscoroutine(object: object) -> TypeGuard[CoroutineType[Any, Any, Any]]: ... def isawaitable(object: object) -> TypeGuard[Awaitable[Any]]: ... if sys.version_info >= (3, 8): + @overload + def isasyncgenfunction(obj: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... @overload def isasyncgenfunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... @overload def isasyncgenfunction(obj: object) -> TypeGuard[Callable[..., AsyncGeneratorType[Any, Any]]]: ... else: + @overload + def isasyncgenfunction(object: Callable[..., AsyncGenerator[Any, Any]]) -> bool: ... @overload def isasyncgenfunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, AsyncGeneratorType[Any, Any]]]: ... @overload From 6c37648825115f3998df73f356be1172f169e6c2 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sun, 12 Jun 2022 13:14:38 +0100 Subject: [PATCH 4/5] More experiment (and tests) --- stdlib/asyncio/coroutines.pyi | 15 ++++++------ stdlib/inspect.pyi | 7 +++++- test_cases/stdlib/asyncio/test_coroutines.py | 24 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 test_cases/stdlib/asyncio/test_coroutines.py diff --git a/stdlib/asyncio/coroutines.pyi b/stdlib/asyncio/coroutines.pyi index 7438f9bd816a..3c131c73e124 100644 --- a/stdlib/asyncio/coroutines.pyi +++ b/stdlib/asyncio/coroutines.pyi @@ -1,7 +1,7 @@ import sys import types -from collections.abc import Callable, Coroutine -from typing import Any, overload +from collections.abc import Awaitable, Callable, Coroutine +from typing import Any, TypeVar, overload from typing_extensions import ParamSpec, TypeGuard if sys.version_info >= (3, 11): @@ -11,18 +11,19 @@ elif sys.version_info >= (3, 7): else: __all__ = ["coroutine", "iscoroutinefunction", "iscoroutine"] +_T = TypeVar("_T") +_FunctionT = TypeVar("_FunctionT", bound=Callable[..., Any]) _P = ParamSpec("_P") if sys.version_info < (3, 11): - from typing import TypeVar - - _F = TypeVar("_F", bound=Callable[..., Any]) - def coroutine(func: _F) -> _F: ... + def coroutine(func: _FunctionT) -> _FunctionT: ... @overload def iscoroutinefunction(func: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... @overload -def iscoroutinefunction(func: Callable[_P, Any]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... +def iscoroutinefunction(func: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, _T]]]: ... +@overload +def iscoroutinefunction(func: Callable[_P, object]) -> TypeGuard[Callable[_P, Coroutine[Any, Any, Any]]]: ... @overload def iscoroutinefunction(func: object) -> TypeGuard[Callable[..., Coroutine[Any, Any, Any]]]: ... diff --git a/stdlib/inspect.pyi b/stdlib/inspect.pyi index 39753df09dac..d963a7093d23 100644 --- a/stdlib/inspect.pyi +++ b/stdlib/inspect.pyi @@ -135,6 +135,7 @@ if sys.version_info >= (3, 11): ] _P = ParamSpec("_P") +_T = TypeVar("_T") _T_cont = TypeVar("_T_cont", contravariant=True) _V_cont = TypeVar("_V_cont", contravariant=True) @@ -191,7 +192,9 @@ if sys.version_info >= (3, 8): @overload def iscoroutinefunction(obj: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... @overload - def iscoroutinefunction(obj: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... + def iscoroutinefunction(obj: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, _T]]]: ... + @overload + def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... @overload def iscoroutinefunction(obj: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... @@ -205,6 +208,8 @@ else: @overload def iscoroutinefunction(object: Callable[..., Coroutine[Any, Any, Any]]) -> bool: ... @overload + def iscoroutinefunction(object: Callable[_P, Awaitable[_T]]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, _T]]]: ... + @overload def iscoroutinefunction(object: Callable[_P, Any]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ... @overload def iscoroutinefunction(object: object) -> TypeGuard[Callable[..., CoroutineType[Any, Any, Any]]]: ... diff --git a/test_cases/stdlib/asyncio/test_coroutines.py b/test_cases/stdlib/asyncio/test_coroutines.py new file mode 100644 index 000000000000..737eddc04cfb --- /dev/null +++ b/test_cases/stdlib/asyncio/test_coroutines.py @@ -0,0 +1,24 @@ +from asyncio import iscoroutinefunction +from collections.abc import Awaitable, Callable, Coroutine +from typing import Any +from typing_extensions import assert_type + + +def test_iscoroutinefunction( + x: Callable[[str, int], Coroutine[str, int, bytes]], + y: Callable[[str, int], Awaitable[bytes]], + z: Callable[[str, int], str | Awaitable[bytes]], + xx: object, +) -> None: + + if iscoroutinefunction(x): + assert_type(x, Callable[[str, int], Coroutine[str, int, bytes]]) + + if iscoroutinefunction(y): + assert_type(y, Callable[[str, int], Coroutine[Any, Any, bytes]]) + + if iscoroutinefunction(z): + assert_type(z, Callable[[str, int], Coroutine[Any, Any, Any]]) + + if iscoroutinefunction(xx): + assert_type(xx, Callable[..., Coroutine[Any, Any, Any]]) From 6b5e903872f02298780125e4b7eb320dc6e0f1cd Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Sun, 12 Jun 2022 13:17:51 +0100 Subject: [PATCH 5/5] Use old syntax --- test_cases/stdlib/asyncio/test_coroutines.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_cases/stdlib/asyncio/test_coroutines.py b/test_cases/stdlib/asyncio/test_coroutines.py index 737eddc04cfb..e2c3d19e6fb6 100644 --- a/test_cases/stdlib/asyncio/test_coroutines.py +++ b/test_cases/stdlib/asyncio/test_coroutines.py @@ -1,13 +1,13 @@ from asyncio import iscoroutinefunction from collections.abc import Awaitable, Callable, Coroutine -from typing import Any +from typing import Any, Union from typing_extensions import assert_type def test_iscoroutinefunction( x: Callable[[str, int], Coroutine[str, int, bytes]], y: Callable[[str, int], Awaitable[bytes]], - z: Callable[[str, int], str | Awaitable[bytes]], + z: Callable[[str, int], Union[str, Awaitable[bytes]]], xx: object, ) -> None: