Skip to content

Commit 1ebe1b4

Browse files
authored
Fix asyncio.gather regression (#8271)
Adding the empty-tuple overload caused major problems for pyright, and that overload only deals with an unlikely edge case anyway. Get rid of it, and replace the fallback overload with a more general overload. Fixes #8270.
1 parent de1a79b commit 1ebe1b4

File tree

2 files changed

+58
-44
lines changed

2 files changed

+58
-44
lines changed

stdlib/asyncio/tasks.pyi

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,26 @@ def ensure_future(coro_or_future: Awaitable[_T], *, loop: AbstractEventLoop | No
6666
# of tasks passed; however, Tuple is used similar to the annotation for
6767
# zip() because typing does not support variadic type variables. See
6868
# typing PR #1550 for discussion.
69+
#
70+
# The many type: ignores here are because the overloads overlap,
71+
# but having overlapping overloads is the only way to get acceptable type inference in all edge cases.
6972
if sys.version_info >= (3, 10):
7073
@overload
71-
def gather(*, return_exceptions: bool = ...) -> Future[tuple[()]]: ...
74+
def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = ...) -> Future[tuple[_T1]]: ... # type: ignore[misc]
7275
@overload
73-
def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: Literal[False] = ...) -> Future[tuple[_T1]]: ...
74-
@overload
75-
def gather(
76+
def gather( # type: ignore[misc]
7677
__coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: Literal[False] = ...
7778
) -> Future[tuple[_T1, _T2]]: ...
7879
@overload
79-
def gather(
80+
def gather( # type: ignore[misc]
8081
__coro_or_future1: _FutureLike[_T1],
8182
__coro_or_future2: _FutureLike[_T2],
8283
__coro_or_future3: _FutureLike[_T3],
8384
*,
8485
return_exceptions: Literal[False] = ...,
8586
) -> Future[tuple[_T1, _T2, _T3]]: ...
8687
@overload
87-
def gather(
88+
def gather( # type: ignore[misc]
8889
__coro_or_future1: _FutureLike[_T1],
8990
__coro_or_future2: _FutureLike[_T2],
9091
__coro_or_future3: _FutureLike[_T3],
@@ -93,7 +94,7 @@ if sys.version_info >= (3, 10):
9394
return_exceptions: Literal[False] = ...,
9495
) -> Future[tuple[_T1, _T2, _T3, _T4]]: ...
9596
@overload
96-
def gather(
97+
def gather( # type: ignore[misc]
9798
__coro_or_future1: _FutureLike[_T1],
9899
__coro_or_future2: _FutureLike[_T2],
99100
__coro_or_future3: _FutureLike[_T3],
@@ -103,21 +104,21 @@ if sys.version_info >= (3, 10):
103104
return_exceptions: Literal[False] = ...,
104105
) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ...
105106
@overload
106-
def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ...
107+
def gather(__coro_or_future1: _FutureLike[_T1], *, return_exceptions: bool) -> Future[tuple[_T1 | BaseException]]: ... # type: ignore[misc]
107108
@overload
108-
def gather(
109+
def gather( # type: ignore[misc]
109110
__coro_or_future1: _FutureLike[_T1], __coro_or_future2: _FutureLike[_T2], *, return_exceptions: bool
110111
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ...
111112
@overload
112-
def gather(
113+
def gather( # type: ignore[misc]
113114
__coro_or_future1: _FutureLike[_T1],
114115
__coro_or_future2: _FutureLike[_T2],
115116
__coro_or_future3: _FutureLike[_T3],
116117
*,
117118
return_exceptions: bool,
118119
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ...
119120
@overload
120-
def gather(
121+
def gather( # type: ignore[misc]
121122
__coro_or_future1: _FutureLike[_T1],
122123
__coro_or_future2: _FutureLike[_T2],
123124
__coro_or_future3: _FutureLike[_T3],
@@ -126,7 +127,7 @@ if sys.version_info >= (3, 10):
126127
return_exceptions: bool,
127128
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ...
128129
@overload
129-
def gather(
130+
def gather( # type: ignore[misc]
130131
__coro_or_future1: _FutureLike[_T1],
131132
__coro_or_future2: _FutureLike[_T2],
132133
__coro_or_future3: _FutureLike[_T3],
@@ -138,34 +139,23 @@ if sys.version_info >= (3, 10):
138139
tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException]
139140
]: ...
140141
@overload
141-
def gather(
142-
__coro_or_future1: _FutureLike[Any],
143-
__coro_or_future2: _FutureLike[Any],
144-
__coro_or_future3: _FutureLike[Any],
145-
__coro_or_future4: _FutureLike[Any],
146-
__coro_or_future5: _FutureLike[Any],
147-
__coro_or_future6: _FutureLike[Any],
148-
*coros_or_futures: _FutureLike[Any],
149-
return_exceptions: bool = ...,
150-
) -> Future[list[Any]]: ...
142+
def gather(*coros_or_futures: _FutureLike[Any], return_exceptions: bool = ...) -> Future[list[Any]]: ... # type: ignore[misc]
151143

152144
else:
153145
@overload
154-
def gather(*, loop: AbstractEventLoop | None = ..., return_exceptions: bool = ...) -> Future[tuple[()]]: ...
155-
@overload
156-
def gather(
146+
def gather( # type: ignore[misc]
157147
__coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = ..., return_exceptions: Literal[False] = ...
158148
) -> Future[tuple[_T1]]: ...
159149
@overload
160-
def gather(
150+
def gather( # type: ignore[misc]
161151
__coro_or_future1: _FutureLike[_T1],
162152
__coro_or_future2: _FutureLike[_T2],
163153
*,
164154
loop: AbstractEventLoop | None = ...,
165155
return_exceptions: Literal[False] = ...,
166156
) -> Future[tuple[_T1, _T2]]: ...
167157
@overload
168-
def gather(
158+
def gather( # type: ignore[misc]
169159
__coro_or_future1: _FutureLike[_T1],
170160
__coro_or_future2: _FutureLike[_T2],
171161
__coro_or_future3: _FutureLike[_T3],
@@ -174,7 +164,7 @@ else:
174164
return_exceptions: Literal[False] = ...,
175165
) -> Future[tuple[_T1, _T2, _T3]]: ...
176166
@overload
177-
def gather(
167+
def gather( # type: ignore[misc]
178168
__coro_or_future1: _FutureLike[_T1],
179169
__coro_or_future2: _FutureLike[_T2],
180170
__coro_or_future3: _FutureLike[_T3],
@@ -184,7 +174,7 @@ else:
184174
return_exceptions: Literal[False] = ...,
185175
) -> Future[tuple[_T1, _T2, _T3, _T4]]: ...
186176
@overload
187-
def gather(
177+
def gather( # type: ignore[misc]
188178
__coro_or_future1: _FutureLike[_T1],
189179
__coro_or_future2: _FutureLike[_T2],
190180
__coro_or_future3: _FutureLike[_T3],
@@ -195,19 +185,19 @@ else:
195185
return_exceptions: Literal[False] = ...,
196186
) -> Future[tuple[_T1, _T2, _T3, _T4, _T5]]: ...
197187
@overload
198-
def gather(
188+
def gather( # type: ignore[misc]
199189
__coro_or_future1: _FutureLike[_T1], *, loop: AbstractEventLoop | None = ..., return_exceptions: bool
200190
) -> Future[tuple[_T1 | BaseException]]: ...
201191
@overload
202-
def gather(
192+
def gather( # type: ignore[misc]
203193
__coro_or_future1: _FutureLike[_T1],
204194
__coro_or_future2: _FutureLike[_T2],
205195
*,
206196
loop: AbstractEventLoop | None = ...,
207197
return_exceptions: bool,
208198
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException]]: ...
209199
@overload
210-
def gather(
200+
def gather( # type: ignore[misc]
211201
__coro_or_future1: _FutureLike[_T1],
212202
__coro_or_future2: _FutureLike[_T2],
213203
__coro_or_future3: _FutureLike[_T3],
@@ -216,7 +206,7 @@ else:
216206
return_exceptions: bool,
217207
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException]]: ...
218208
@overload
219-
def gather(
209+
def gather( # type: ignore[misc]
220210
__coro_or_future1: _FutureLike[_T1],
221211
__coro_or_future2: _FutureLike[_T2],
222212
__coro_or_future3: _FutureLike[_T3],
@@ -226,7 +216,7 @@ else:
226216
return_exceptions: bool,
227217
) -> Future[tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException]]: ...
228218
@overload
229-
def gather(
219+
def gather( # type: ignore[misc]
230220
__coro_or_future1: _FutureLike[_T1],
231221
__coro_or_future2: _FutureLike[_T2],
232222
__coro_or_future3: _FutureLike[_T3],
@@ -239,16 +229,8 @@ else:
239229
tuple[_T1 | BaseException, _T2 | BaseException, _T3 | BaseException, _T4 | BaseException, _T5 | BaseException]
240230
]: ...
241231
@overload
242-
def gather(
243-
__coro_or_future1: _FutureLike[Any],
244-
__coro_or_future2: _FutureLike[Any],
245-
__coro_or_future3: _FutureLike[Any],
246-
__coro_or_future4: _FutureLike[Any],
247-
__coro_or_future5: _FutureLike[Any],
248-
__coro_or_future6: _FutureLike[Any],
249-
*coros_or_futures: _FutureLike[Any],
250-
loop: AbstractEventLoop | None = ...,
251-
return_exceptions: bool = ...,
232+
def gather( # type: ignore[misc]
233+
*coros_or_futures: _FutureLike[Any], loop: AbstractEventLoop | None = ..., return_exceptions: bool = ...
252234
) -> Future[list[Any]]: ...
253235

254236
def run_coroutine_threadsafe(coro: _FutureLike[_T], loop: AbstractEventLoop) -> concurrent.futures.Future[_T]: ...
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import asyncio
2+
from typing import Any, Awaitable, List, Tuple, Union
3+
from typing_extensions import assert_type
4+
5+
6+
async def coro1() -> int:
7+
return 42
8+
9+
10+
async def coro2() -> str:
11+
return "spam"
12+
13+
14+
async def test_gather(awaitable1: Awaitable[int], awaitable2: Awaitable[str]) -> None:
15+
a = await asyncio.gather(awaitable1)
16+
assert_type(a, Tuple[int])
17+
18+
b = await asyncio.gather(awaitable1, awaitable2, return_exceptions=True)
19+
assert_type(b, Tuple[Union[int, BaseException], Union[str, BaseException]])
20+
21+
c = await asyncio.gather(awaitable1, awaitable2, awaitable1, awaitable1, awaitable1, awaitable1)
22+
assert_type(c, List[Any])
23+
24+
awaitables_list: List[Awaitable[int]] = [awaitable1]
25+
d = await asyncio.gather(*awaitables_list)
26+
assert_type(d, List[Any])
27+
28+
e = await asyncio.gather()
29+
assert_type(e, List[Any])
30+
31+
32+
asyncio.run(test_gather(coro1(), coro2()))

0 commit comments

Comments
 (0)