Skip to content

Commit 9631e2b

Browse files
OsmnvAslanclaude
andcommitted
gh-157856: Fix asyncio.as_completed() outside a running event loop
Guard the awaited-by tracking in _AsCompletedIterator the same way gather() does, so that creating the iterator outside of a running event loop and driving it with loop.run_until_complete() works again. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 5962fc2 commit 9631e2b

3 files changed

Lines changed: 26 additions & 1 deletion

File tree

‎Lib/asyncio/tasks.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,13 @@ def __init__(self, aws, timeout):
578578
self._timeout_handle = None
579579

580580
loop = events.get_event_loop()
581-
self._cur_task = current_task()
581+
# The iterator may be created outside of a running event loop and
582+
# then driven with loop.run_until_complete(), in which case there
583+
# is no current task to record as the waiter.
584+
if events._get_running_loop() is loop:
585+
self._cur_task = current_task(loop)
586+
else:
587+
self._cur_task = None
582588
todo = {ensure_future(aw, loop=loop) for aw in set(aws)}
583589
for f in todo:
584590
f.add_done_callback(self._handle_completion)

‎Lib/test/test_asyncio/test_tasks.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,22 @@ async def coro():
17701770
futs = asyncio.as_completed([a])
17711771
list(futs)
17721772

1773+
def test_as_completed_outside_running_loop(self):
1774+
# gh-157856: as_completed() must not require a running event loop
1775+
# when the iterator is created, only when it is driven.
1776+
loop = self.new_test_loop()
1777+
self.addCleanup(asyncio.set_event_loop, None)
1778+
asyncio.set_event_loop(loop)
1779+
1780+
async def coro(v):
1781+
await asyncio.sleep(0)
1782+
return v
1783+
1784+
tasks = [loop.create_task(coro(v)) for v in (1, 2)]
1785+
futs = asyncio.as_completed(tasks)
1786+
results = [loop.run_until_complete(f) for f in futs]
1787+
self.assertEqual(sorted(results), [1, 2])
1788+
17731789
def test_as_completed_coroutine_use_running_loop(self):
17741790
loop = self.new_test_loop()
17751791

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`asyncio.as_completed` raising :exc:`RuntimeError` ("no running
2+
event loop") when the iterator is created outside of a running event loop and
3+
driven with :meth:`loop.run_until_complete() <asyncio.loop.run_until_complete>`.

0 commit comments

Comments
 (0)