mirror of
https://github.com/python/cpython.git
synced 2024-11-27 11:55:13 +08:00
bpo-34790: Remove passing coroutine objects to asyncio.wait() (GH-31964)
Co-authored-by: Yury Selivanov <yury@edgedb.com>
This commit is contained in:
parent
33698e8ff4
commit
903f0a02c1
@ -534,7 +534,7 @@ Waiting Primitives
|
||||
|
||||
.. coroutinefunction:: wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
|
||||
|
||||
Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
|
||||
Run :class:`~asyncio.Future` and :class:`~asyncio.Task` instances in the *aws*
|
||||
iterable concurrently and block until the condition specified
|
||||
by *return_when*.
|
||||
|
||||
@ -577,51 +577,11 @@ Waiting Primitives
|
||||
Unlike :func:`~asyncio.wait_for`, ``wait()`` does not cancel the
|
||||
futures when a timeout occurs.
|
||||
|
||||
.. deprecated:: 3.8
|
||||
|
||||
If any awaitable in *aws* is a coroutine, it is automatically
|
||||
scheduled as a Task. Passing coroutines objects to
|
||||
``wait()`` directly is deprecated as it leads to
|
||||
:ref:`confusing behavior <asyncio_example_wait_coroutine>`.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Removed the *loop* parameter.
|
||||
|
||||
.. _asyncio_example_wait_coroutine:
|
||||
.. note::
|
||||
|
||||
``wait()`` schedules coroutines as Tasks automatically and later
|
||||
returns those implicitly created Task objects in ``(done, pending)``
|
||||
sets. Therefore the following code won't work as expected::
|
||||
|
||||
async def foo():
|
||||
return 42
|
||||
|
||||
coro = foo()
|
||||
done, pending = await asyncio.wait({coro})
|
||||
|
||||
if coro in done:
|
||||
# This branch will never be run!
|
||||
|
||||
Here is how the above snippet can be fixed::
|
||||
|
||||
async def foo():
|
||||
return 42
|
||||
|
||||
task = asyncio.create_task(foo())
|
||||
done, pending = await asyncio.wait({task})
|
||||
|
||||
if task in done:
|
||||
# Everything will work as expected now.
|
||||
|
||||
.. deprecated-removed:: 3.8 3.11
|
||||
|
||||
Passing coroutine objects to ``wait()`` directly is
|
||||
deprecated.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Removed the *loop* parameter.
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
Passing coroutine objects to ``wait()`` directly is forbidden.
|
||||
|
||||
.. function:: as_completed(aws, *, timeout=None)
|
||||
|
||||
|
@ -387,7 +387,7 @@ ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
|
||||
|
||||
|
||||
async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
|
||||
"""Wait for the Futures and coroutines given by fs to complete.
|
||||
"""Wait for the Futures or Tasks given by fs to complete.
|
||||
|
||||
The fs iterable must not be empty.
|
||||
|
||||
@ -405,22 +405,16 @@ async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
|
||||
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
|
||||
raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
|
||||
if not fs:
|
||||
raise ValueError('Set of coroutines/Futures is empty.')
|
||||
raise ValueError('Set of Tasks/Futures is empty.')
|
||||
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
|
||||
raise ValueError(f'Invalid return_when value: {return_when}')
|
||||
|
||||
loop = events.get_running_loop()
|
||||
|
||||
fs = set(fs)
|
||||
|
||||
if any(coroutines.iscoroutine(f) for f in fs):
|
||||
warnings.warn("The explicit passing of coroutine objects to "
|
||||
"asyncio.wait() is deprecated since Python 3.8, and "
|
||||
"scheduled for removal in Python 3.11.",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
fs = {ensure_future(f, loop=loop) for f in fs}
|
||||
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
|
||||
|
||||
loop = events.get_running_loop()
|
||||
return await _wait(fs, timeout, return_when, loop)
|
||||
|
||||
|
||||
|
@ -997,13 +997,12 @@ class BaseTaskTests:
|
||||
|
||||
async def coro(s):
|
||||
return s
|
||||
c = coro('test')
|
||||
c = self.loop.create_task(coro('test'))
|
||||
task = self.new_task(
|
||||
self.loop,
|
||||
asyncio.wait([c, c, coro('spam')]))
|
||||
asyncio.wait([c, c, self.loop.create_task(coro('spam'))]))
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
done, pending = self.loop.run_until_complete(task)
|
||||
done, pending = self.loop.run_until_complete(task)
|
||||
|
||||
self.assertFalse(pending)
|
||||
self.assertEqual(set(f.result() for f in done), {'test', 'spam'})
|
||||
@ -1380,11 +1379,9 @@ class BaseTaskTests:
|
||||
async def test():
|
||||
futs = list(asyncio.as_completed(fs))
|
||||
self.assertEqual(len(futs), 2)
|
||||
waiter = asyncio.wait(futs)
|
||||
# Deprecation from passing coros in futs to asyncio.wait()
|
||||
with self.assertWarns(DeprecationWarning) as cm:
|
||||
done, pending = await waiter
|
||||
self.assertEqual(cm.warnings[0].filename, __file__)
|
||||
done, pending = await asyncio.wait(
|
||||
[asyncio.ensure_future(fut) for fut in futs]
|
||||
)
|
||||
self.assertEqual(set(f.result() for f in done), {'a', 'b'})
|
||||
|
||||
loop = self.new_test_loop(gen)
|
||||
@ -1434,21 +1431,6 @@ class BaseTaskTests:
|
||||
|
||||
loop.run_until_complete(test())
|
||||
|
||||
def test_as_completed_coroutine_use_global_loop(self):
|
||||
# Deprecated in 3.10
|
||||
async def coro():
|
||||
return 42
|
||||
|
||||
loop = self.new_test_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
self.addCleanup(asyncio.set_event_loop, None)
|
||||
futs = asyncio.as_completed([coro()])
|
||||
with self.assertWarns(DeprecationWarning) as cm:
|
||||
futs = list(futs)
|
||||
self.assertEqual(cm.warnings[0].filename, __file__)
|
||||
self.assertEqual(len(futs), 1)
|
||||
self.assertEqual(loop.run_until_complete(futs[0]), 42)
|
||||
|
||||
def test_sleep(self):
|
||||
|
||||
def gen():
|
||||
@ -1751,7 +1733,7 @@ class BaseTaskTests:
|
||||
async def outer():
|
||||
nonlocal proof
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
d, p = await asyncio.wait([inner()])
|
||||
d, p = await asyncio.wait([asyncio.create_task(inner())])
|
||||
proof += 100
|
||||
|
||||
f = asyncio.ensure_future(outer(), loop=self.loop)
|
||||
@ -3220,29 +3202,6 @@ class SleepTests(test_utils.TestCase):
|
||||
self.assertEqual(result, 11)
|
||||
|
||||
|
||||
class WaitTests(test_utils.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.loop = asyncio.new_event_loop()
|
||||
self.set_event_loop(self.loop)
|
||||
|
||||
def tearDown(self):
|
||||
self.loop.close()
|
||||
self.loop = None
|
||||
super().tearDown()
|
||||
|
||||
def test_coro_is_deprecated_in_wait(self):
|
||||
# Remove test when passing coros to asyncio.wait() is removed in 3.11
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.loop.run_until_complete(
|
||||
asyncio.wait([coroutine_function()]))
|
||||
|
||||
task = self.loop.create_task(coroutine_function())
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.loop.run_until_complete(
|
||||
asyncio.wait([task, coroutine_function()]))
|
||||
|
||||
|
||||
class CompatibilityTests(test_utils.TestCase):
|
||||
# Tests for checking a bridge between old-styled coroutines
|
||||
# and async/await syntax
|
||||
|
@ -0,0 +1 @@
|
||||
Remove passing coroutine objects to :func:`asyncio.wait`.
|
Loading…
Reference in New Issue
Block a user