8000 bpo-34790: Remove passing coroutine objects to asyncio.wait() (GH-31964) · python/cpython@903f0a0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 903f0a0

Browse files
asvetlov1st1
andauthored
bpo-34790: Remove passing coroutine objects to asyncio.wait() (GH-31964)
Co-authored-by: Yury Selivanov <yury@edgedb.com>
1 parent 33698e8 commit 903f0a0

File tree

4 files changed

+15
-101
lines changed

4 files changed

+15
-101
lines changed

Doc/library/asyncio-task.rst

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ Waiting Primitives
534534

535535
.. coroutinefunction:: wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
536536

537-
Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
537+
Run :class:`~asyncio.Future` and :class:`~asyncio.Task` instances in the *aws*
538538
iterable concurrently and block until the condition specified
539539
by *return_when*.
540540

@@ -577,51 +577,11 @@ Waiting Primitives
577577
Unlike :func:`~asyncio.wait_for`, ``wait()`` does not cancel the
578578
futures when a timeout occurs.
579579

580-
.. deprecated:: 3.8
581-
582-
If any awaitable in *aws* is a coroutine, it is automatically
583-
scheduled as a Task. Passing coroutines objects to
584-
``wait()`` directly is deprecated as it leads to
585-
:ref:`confusing behavior <asyncio_example_wait_coroutine>`.
586-
587-
.. versionchanged:: 3.10
588-
Removed the *loop* parameter.
589-
590-
.. _asyncio_example_wait_coroutine:
591-
.. note::
592-
593-
``wait()`` schedules coroutines as Tasks automatically and later
594-
returns those implicitly created Task objects in ``(done, pending)``
595-
sets. Therefore the following code won't work as expected::
596-
597-
async def foo():
598-
return 42
599-
600-
coro = foo()
601-
done, pending = await asyncio.wait({coro})
602-
603-
if coro in done:
604-
# This branch will never be run!
605-
606-
Here is how the above snippet can be fixed::
607-
608-
async def foo():
609-
return 42
610-
611-
task = asyncio.create_task(foo())
612-
done, pending = await asyncio.wait({task})
613-
614-
if task in done:
615-
# Everything will work as expected now.
616-
617-
.. deprecated-removed:: 3.8 3.11
618-
619-
Passing coroutine objects to ``wait()`` directly is
620-
deprecated.
621-
622580
.. versionchanged:: 3.10
623581
Removed the *loop* parameter.
624582

583+
.. versionchanged:: 3.11
584+
Passing coroutine objects to ``wait()`` directly is forbidden.
625585

626586
.. function:: as_completed(aws, *, timeout=None)
627587

Lib/asyncio/tasks.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def create_task(coro, *, name=None, context=None):
387387

388388

389389
async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
390-
"""Wait for the Futures and coroutines given by fs to complete.
390+
"""Wait for the Futures or Tasks given by fs to complete.
391391
392392
The fs iterable must not be empty.
393393
@@ -405,22 +405,16 @@ async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
405405
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
406406
raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
407407
if not fs:
408-
raise ValueError('Set of coroutines/Futures is empty.')
408+
raise ValueError('Set of Tasks/Futures is empty.')
409409
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
410410
raise ValueError(f'Invalid return_when value: {return_when}')
411411

412-
loop = events.get_running_loop()
413-
414412
fs = set(fs)
415413

416414
if any(coroutines.iscoroutine(f) for f in fs):
417-
warnings.warn("The explicit passing of coroutine objects to "
418-
"asyncio.wait() is deprecated since Python 3.8, and "
419-
"scheduled for removal in Python 3.11.",
420-
DeprecationWarning, stacklevel=2)
421-
422-
fs = {ensure_future(f, loop=loop) for f in fs}
415+
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
423416

417+
loop = events.get_running_loop()
424418
return await _wait(fs, timeout, return_when, loop)
425419

426420

Lib/test/test_asyncio/test_tasks.py

Lines changed: 7 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -997,13 +997,12 @@ def test_wait_duplicate_coroutines(self):
997997

998998
async def coro(s):
999999
return s
1000-
c = coro('test')
1000+
c = self.loop.create_task(coro('test'))
10011001
task = self.new_task(
10021002
self.loop,
1003-
asyncio.wait([c, c, coro('spam')]))
1003+
asyncio.wait([c, c, self.loop.create_task(coro('spam'))]))
10041004

1005-
with self.assertWarns(DeprecationWarning):
1006-
done, pending = self.loop.run_until_complete(task)
1005+
done, pending = self.loop.run_until_complete(task)
10071006

10081007
self.assertFalse(pending)
10091008
self.assertEqual(set(f.result() for f in done), {'test', 'spam'})
@@ -1380,11 +1379,9 @@ def gen():
13801379
async def test():
13811380
futs = list(asyncio.as_completed(fs))
13821381
self.assertEqual(len(futs), 2)
1383-
waiter = asyncio.wait(futs)
1384-
# Deprecation from passing coros in futs to asyncio.wait()
1385-
with self.assertWarns(DeprecationWarning) as cm:
1386-
done, pending = await waiter
1387-
self.assertEqual(cm.warnings[0].filename, __file__)
1382+
done, pending = await asyncio.wait(
1383+
[asyncio.ensure_future(fut) for fut in futs]
1384+
)
13881385
self.assertEqual(set(f.result() for f in done), {'a', 'b'})
13891386

13901387
loop = self.new_test_loop(gen)
@@ -1434,21 +1431,6 @@ async def test():
14341431

14351432
loop.run_until_complete(test())
14361433

1437-
def test_as_completed_coroutine_use_global_loop(self):
1438-
# Deprecated in 3.10
1439-
async def coro():
1440-
return 42
1441-
1442-
loop = self.new_test_loop()
1443-
asyncio.set_event_loop(loop)
1444-
self.addCleanup(asyncio.set_event_loop, None)
1445-
futs = asyncio.as_completed([coro()])
1446-
with self.assertWarns(DeprecationWarning) as cm:
1447-
futs = list(futs)
1448-
self.assertEqual(cm.warnings[0].filename, __file__)
1449-
self.assertEqual(len(futs), 1)
1450-
self.assertEqual(loop.run_until_complete(futs[0]), 42)
1451-
14521434
def test_sleep(self):
14531435

14541436
def gen():
@@ -1751,7 +1733,7 @@ async def inner():
17511733
async def outer():
17521734
nonlocal proof
17531735
with self.assertWarns(DeprecationWarning):
1754-
d, p = await asyncio.wait([inner()])
1736+
d, p = await asyncio.wait([asyncio.create_task(inner())])
17551737
proof += 100
17561738

17571739
f = asyncio.ensure_future(outer(), loop=self.loop)
@@ -3220,29 +3202,6 @@ async def coro():
32203202
self.assertEqual(result, 11)
32213203

32223204

3223-
class WaitTests(test_utils.TestCase):
3224-
def setUp(self):
3225-
super().setUp()
3226-
self.loop = asyncio.new_event_loop()
3227-
self.set_event_loop(self.loop)
3228-
3229-
def tearDown(self):
3230-
self.loop.close()
3231-
self.loop = None
3232-
super().tearDown()
3233-
3234-
def test_coro_is_deprecated_in_wait(self):
3235-
# Remove test when passing coros to asyncio.wait() is removed in 3.11
3236-
with self.assertWarns(DeprecationWarning):
3237-
self.loop.run_until_complete(
3238-
asyncio.wait([coroutine_function()]))
3239-
3240-
task = self.loop.create_task(coroutine_function())
3241-
with self.assertWarns(DeprecationWarning):
3242-
self.loop.run_until_complete(
3243-
asyncio.wait([task, coroutine_function()]))
3244-
3245-
32463205
class CompatibilityTests(test_utils.TestCase):
32473206
# Tests for checking a bridge between old-styled coroutines
32483207
# and async/await syntax
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove passing coroutine objects to :func:`asyncio.wait`.

0 commit comments

Comments
 (0)
0