]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-117536: GH-117894: fix athrow().throw(...) unawaited warning (GH-117851)
authorThomas Grainger <tagrain@gmail.com>
Wed, 24 Apr 2024 12:20:19 +0000 (13:20 +0100)
committerGitHub <noreply@github.com>
Wed, 24 Apr 2024 12:20:19 +0000 (14:20 +0200)
Lib/test/test_asyncgen.py
Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst [new file with mode: 0644]
Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst [new file with mode: 0644]
Objects/genobject.c

index 39605dca3886c8f69f877c6efea7fd23c8b2f5fe..a1e9e1b89c6a2cf350cc74bf059cf423705a6431 100644 (file)
@@ -399,9 +399,8 @@ class AsyncGenTest(unittest.TestCase):
 
         with self.assertWarns(DeprecationWarning):
             x = gen().athrow(GeneratorExit, GeneratorExit(), None)
-        with self.assertWarnsRegex(RuntimeWarning,
-                f"coroutine method 'athrow' of '{gen.__qualname__}' "
-                f"was never awaited"):
+        with self.assertRaises(GeneratorExit):
+            x.send(None)
             del x
             gc_collect()
 
@@ -1572,11 +1571,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
         self.assertIsInstance(message['exception'], ZeroDivisionError)
         self.assertIn('unhandled exception during asyncio.run() shutdown',
                       message['message'])
-        with self.assertWarnsRegex(RuntimeWarning,
-                f"coroutine method 'aclose' of '{async_iterate.__qualname__}' "
-                f"was never awaited"):
-            del message, messages
-            gc_collect()
 
     def test_async_gen_expression_01(self):
         async def arange(n):
@@ -1630,10 +1624,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
         asyncio.run(main())
 
         self.assertEqual([], messages)
-        with self.assertWarnsRegex(RuntimeWarning,
-                f"coroutine method 'aclose' of '{async_iterate.__qualname__}' "
-                f"was never awaited"):
-            gc_collect()
 
     def test_async_gen_await_same_anext_coro_twice(self):
         async def async_iterate():
@@ -1671,6 +1661,62 @@ class AsyncGenAsyncioTest(unittest.TestCase):
 
         self.loop.run_until_complete(run())
 
+    def test_async_gen_throw_same_aclose_coro_twice(self):
+        async def async_iterate():
+            yield 1
+            yield 2
+
+        it = async_iterate()
+        nxt = it.aclose()
+        with self.assertRaises(StopIteration):
+            nxt.throw(GeneratorExit)
+
+        with self.assertRaisesRegex(
+            RuntimeError,
+            r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+        ):
+            nxt.throw(GeneratorExit)
+
+    def test_async_gen_throw_custom_same_aclose_coro_twice(self):
+        async def async_iterate():
+            yield 1
+            yield 2
+
+        it = async_iterate()
+
+        class MyException(Exception):
+            pass
+
+        nxt = it.aclose()
+        with self.assertRaises(MyException):
+            nxt.throw(MyException)
+
+        with self.assertRaisesRegex(
+            RuntimeError,
+            r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+        ):
+            nxt.throw(MyException)
+
+    def test_async_gen_throw_custom_same_athrow_coro_twice(self):
+        async def async_iterate():
+            yield 1
+            yield 2
+
+        it = async_iterate()
+
+        class MyException(Exception):
+            pass
+
+        nxt = it.athrow(MyException)
+        with self.assertRaises(MyException):
+            nxt.throw(MyException)
+
+        with self.assertRaisesRegex(
+            RuntimeError,
+            r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+        ):
+            nxt.throw(MyException)
+
     def test_async_gen_aclose_twice_with_different_coros(self):
         # Regression test for https://bugs.python.org/issue39606
         async def async_iterate():
@@ -1752,6 +1798,19 @@ class TestUnawaitedWarnings(unittest.TestCase):
             g.aclose()
             gc_collect()
 
+    def test_aclose_throw(self):
+        async def gen():
+            return
+            yield
+
+        class MyException(Exception):
+            pass
+
+        g = gen()
+        with self.assertRaises(MyException):
+            g.aclose().throw(MyException)
+            del g
+            gc_collect()
 
 
 if __name__ == "__main__":
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-13-16-55-53.gh-issue-117536.xkVbfv.rst
new file mode 100644 (file)
index 0000000..2492fd1
--- /dev/null
@@ -0,0 +1 @@
+Fix a :exc:`RuntimeWarning` when calling ``agen.aclose().throw(Exception)``.
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst
new file mode 100644 (file)
index 0000000..bd32500
--- /dev/null
@@ -0,0 +1 @@
+Prevent ``agen.aclose()`` objects being re-used after ``.throw()``.
index 8d1dbb72ba9ec212032dc3d84aa80815cb41a7fe..5d7da49cfca1668181be990b0439c2f25d66e6d9 100644 (file)
@@ -2208,7 +2208,11 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na
 
     retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs);
     if (o->agt_args) {
-        return async_gen_unwrap_value(o->agt_gen, retval);
+        retval = async_gen_unwrap_value(o->agt_gen, retval);
+        if (retval == NULL) {
+            o->agt_state = AWAITABLE_STATE_CLOSED;
+        }
+        return retval;
     } else {
         /* aclose() mode */
         if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
@@ -2218,6 +2222,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na
             PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
             return NULL;
         }
+        if (retval == NULL) {
+            o->agt_state = AWAITABLE_STATE_CLOSED;
+        }
         if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
             PyErr_ExceptionMatches(PyExc_GeneratorExit))
         {