diff --git a/stdlib/asyncio/tasks.pyi b/stdlib/asyncio/tasks.pyi index a047998c0..d919a0299 100644 --- a/stdlib/asyncio/tasks.pyi +++ b/stdlib/asyncio/tasks.pyi @@ -66,17 +66,18 @@ def ensure_future(coro_or_future: Awaitable[_T], *, loop: AbstractEventLoop | No # of tasks passed; however, Tuple is used similar to the annotation for # zip() because typing does not support variadic type variables. See # typing PR #1550 for discussion. +# +# The many type: ignores here are because the overloads overlap, +# but having overlapping overloads is the only way to get acceptable type inference in all edge cases. if sys.version_info >= (3, 10): @overload - def gather(*, return_exceptions: bool = ...) -> Future[tuple[()]]: ... + def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = ...) -> Future[tuple[_T1]]: ... # type: ignore[misc] @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = ...) -> Future[tuple[_T1]]: ... - @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: Literal[False] = ... ) -> Future[tuple[_T1, _T2]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -84,7 +85,7 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -93,7 +94,7 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -103,13 +104,13 @@ if sys.version_info >= (3, 10): return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload - def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... + def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[misc] @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: bool ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -117,7 +118,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -126,7 +127,7 @@ if sys.version_info >= (3, 10): return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -138,26 +139,15 @@ if sys.version_info >= (3, 10): tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather( - __coro_or_future1: _FutureLike[Any], - __coro_or_future2: _FutureLike[Any], - __coro_or_future3: _FutureLike[Any], - __coro_or_future4: _FutureLike[Any], - __coro_or_future5: _FutureLike[Any], - __coro_or_future6: _FutureLike[Any], - *coros_or_futures: _FutureLike[Any], - return_exceptions: bool = ..., - ) -> Future[list[Any]]: ... + def gather(*coros_or_futures: _FutureLike[Any], return_exceptions: bool = ...) -> Future[list[Any]]: ... # type: ignore[misc] else: @overload - def gather(*, loop: AbstractEventLoop | None = ..., return_exceptions: bool = ...) -> Future[tuple[()]]: ... - @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = ..., return_exceptions: Literal[False] = ... ) -> Future[tuple[_T1]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, @@ -165,7 +155,7 @@ else: return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -174,7 +164,7 @@ else: return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -184,7 +174,7 @@ else: return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3, _T4]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -195,11 +185,11 @@ else: return_exceptions: Literal[False] = ..., ) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = ..., return_exceptions: bool ) -> Future[tuple[_T1 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, @@ -207,7 +197,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -216,7 +206,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -226,7 +216,7 @@ else: return_exceptions: bool, ) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ... @overload - def gather( + def gather( # type: ignore[misc] __coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], __coro_or_future3: _FutureLike[_T3], @@ -239,16 +229,8 @@ else: tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException] ]: ... @overload - def gather( - __coro_or_future1: _FutureLike[Any], - __coro_or_future2: _FutureLike[Any], - __coro_or_future3: _FutureLike[Any], - __coro_or_future4: _FutureLike[Any], - __coro_or_future5: _FutureLike[Any], - __coro_or_future6: _FutureLike[Any], - *coros_or_futures: _FutureLike[Any], - loop: AbstractEventLoop | None = ..., - return_exceptions: bool = ..., + def gather( # type: ignore[misc] + *coros_or_futures: _FutureLike[Any], loop: AbstractEventLoop | None = ..., return_exceptions: bool = ... ) -> Future[list[Any]]: ... def run_coroutine_threadsafe(coro: _FutureLike[_T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ... diff --git a/test_cases/stdlib/asyncio/test_gather.py b/test_cases/stdlib/asyncio/test_gather.py new file mode 100644 index 000000000..c64af0b11 --- /dev/null +++ b/test_cases/stdlib/asyncio/test_gather.py @@ -0,0 +1,32 @@ +import asyncio +from typing import Any, Awaitable, List, Tuple, Union +from typing_extensions import assert_type + + +async def coro1() -> int: + return 42 + + +async def coro2() -> str: + return "spam" + + +async def test_gather(awaitable1: Awaitable[int], awaitable2: Awaitable[str]) -> None: + a = await asyncio.gather(awaitable1) + assert_type(a, Tuple[int]) + + b = await asyncio.gather(awaitable1, awaitable2, return_exceptions=True) + assert_type(b, Tuple[Union[int, BaseException], Union[str, BaseException]]) + + c = await asyncio.gather(awaitable1, awaitable2, awaitable1, awaitable1, awaitable1, awaitable1) + assert_type(c, List[Any]) + + awaitables_list: List[Awaitable[int]] = [awaitable1] + d = await asyncio.gather(*awaitables_list) + assert_type(d, List[Any]) + + e = await asyncio.gather() + assert_type(e, List[Any]) + + +asyncio.run(test_gather(coro1(), coro2()))