]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-34790: Remove passing coroutine objects to asyncio.wait() (GH-31964)
authorAndrew Svetlov <andrew.svetlov@gmail.com>
Thu, 17 Mar 2022 20:51:40 +0000 (22:51 +0200)
committerGitHub <noreply@github.com>
Thu, 17 Mar 2022 20:51:40 +0000 (22:51 +0200)
Co-authored-by: Yury Selivanov <yury@edgedb.com>
Doc/library/asyncio-task.rst
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS.d/next/Library/2022-03-17-19-38-40.bpo-34790.zQIiVJ.rst [new file with mode: 0644]

index faf5910124f9b7e3dfdee2c350bf39b5e1189349..294f5ab2b22f9d197f12b2077a4c4923e1d516dd 100644 (file)
@@ -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)
 
index 0b5f3226802de065d3b92aa696e97cb443fc6e1c..e876f8d002ce4fd0dba6459f91184d158069a373 100644 (file)
@@ -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)
 
 
index 141c019dcb9cc01b7911b66e78930d34e075ac6e..b86646ee398ae3e4460887c3f3c5998a7d965c4b 100644 (file)
@@ -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 (file)
index 0000000..50a71b5
--- /dev/null
@@ -0,0 +1 @@
+Remove passing coroutine objects to :func:`asyncio.wait`.