diff options
author | Andrew Svetlov <andrew.svetlov@gmail.com> | 2022-03-17 20:51:40 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-17 20:51:40 (GMT) |
commit | 903f0a02c16240dc769a08c30e8d072a4fb09154 (patch) | |
tree | 00a9ff2477f94c7067c9daad481abde0d8d8ff3b | |
parent | 33698e8ff40fcc67df3d95658e87196f8021de6f (diff) | |
download | cpython-903f0a02c16240dc769a08c30e8d072a4fb09154.zip cpython-903f0a02c16240dc769a08c30e8d072a4fb09154.tar.gz cpython-903f0a02c16240dc769a08c30e8d072a4fb09154.tar.bz2 |
bpo-34790: Remove passing coroutine objects to asyncio.wait() (GH-31964)
Co-authored-by: Yury Selivanov <yury@edgedb.com>
-rw-r--r-- | Doc/library/asyncio-task.rst | 46 | ||||
-rw-r--r-- | Lib/asyncio/tasks.py | 14 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_tasks.py | 55 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-03-17-19-38-40.bpo-34790.zQIiVJ.rst | 1 |
4 files changed, 15 insertions, 101 deletions
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index faf5910..294f5ab 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -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) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 0b5f322..e876f8d 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -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) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 141c019..b86646e 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -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 diff --git a/Misc/NEWS.d/next/Library/2022-03-17-19-38-40.bpo-34790.zQIiVJ.rst b/Misc/NEWS.d/next/Library/2022-03-17-19-38-40.bpo-34790.zQIiVJ.rst new file mode 100644 index 0000000..50a71b5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-03-17-19-38-40.bpo-34790.zQIiVJ.rst @@ -0,0 +1 @@ +Remove passing coroutine objects to :func:`asyncio.wait`. |