Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-34790: Remove passing coroutine objects to asyncio.wait() #31964

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 3 additions & 43 deletions Doc/library/asyncio-task.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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*.

Expand Down Expand Up @@ -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)

Expand Down
14 changes: 4 additions & 10 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ def create_task(coro, *, name=None, context=None):


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.

Expand All @@ -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)


Expand Down
55 changes: 7 additions & 48 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -997,13 +997,12 @@ def test_wait_duplicate_coroutines(self):

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'})
Expand Down Expand Up @@ -1380,11 +1379,9 @@ def gen():
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)
Expand Down Expand Up @@ -1434,21 +1431,6 @@ async def test():

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():
Expand Down Expand Up @@ -1751,7 +1733,7 @@ async def inner():
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)
Expand Down Expand Up @@ -3220,29 +3202,6 @@ async def coro():
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove passing coroutine objects to :func:`asyncio.wait`.