]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-117714: implement athrow().close() and asend().close() using throw (GH-117906)
authorThomas Grainger <tagrain@gmail.com>
Mon, 6 May 2024 17:13:15 +0000 (18:13 +0100)
committerGitHub <noreply@github.com>
Mon, 6 May 2024 17:13:15 +0000 (17:13 +0000)
* GH-117714: replace athrow().close() and asend().close() stubs with implimentations

* test athrow().close() and asend().close() raises RuntimeError

* ðŸ“œðŸ¤– Added by blurb_it.

* Update Objects/genobject.c

Co-authored-by: Petr Viktorin <encukou@gmail.com>
---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Petr Viktorin <encukou@gmail.com>
Lib/test/test_asyncgen.py
Misc/NEWS.d/next/Core and Builtins/2024-05-01-07-06-48.gh-issue-117714.Ip_dm5.rst [new file with mode: 0644]
Objects/genobject.c

index 1985ede656e7a05b4f2bfb57a70aecef322d1c35..4f2278bb263681aa90804d9918dcfc5195499e98 100644 (file)
@@ -571,6 +571,54 @@ class AsyncGenTest(unittest.TestCase):
         self.assertTrue(inspect.isawaitable(aclose))
         aclose.close()
 
+    def test_async_gen_asend_close_runtime_error(self):
+        import types
+
+        @types.coroutine
+        def _async_yield(v):
+            return (yield v)
+
+        async def agenfn():
+            try:
+                await _async_yield(None)
+            except GeneratorExit:
+                await _async_yield(None)
+            return
+            yield
+
+        agen = agenfn()
+        gen = agen.asend(None)
+        gen.send(None)
+        with self.assertRaisesRegex(RuntimeError, "coroutine ignored GeneratorExit"):
+            gen.close()
+
+    def test_async_gen_athrow_close_runtime_error(self):
+        import types
+
+        @types.coroutine
+        def _async_yield(v):
+            return (yield v)
+
+        class MyExc(Exception):
+            pass
+
+        async def agenfn():
+            try:
+                yield
+            except MyExc:
+                try:
+                    await _async_yield(None)
+                except GeneratorExit:
+                    await _async_yield(None)
+
+        agen = agenfn()
+        with self.assertRaises(StopIteration):
+            agen.asend(None).send(None)
+        gen = agen.athrow(MyExc)
+        gen.send(None)
+        with self.assertRaisesRegex(RuntimeError, "coroutine ignored GeneratorExit"):
+            gen.close()
+
 
 class AsyncGenAsyncioTest(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-01-07-06-48.gh-issue-117714.Ip_dm5.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-01-07-06-48.gh-issue-117714.Ip_dm5.rst
new file mode 100644 (file)
index 0000000..d8ec242
--- /dev/null
@@ -0,0 +1 @@
+update ``async_generator.athrow().close()`` and ``async_generator.asend().close()`` to close their section of the underlying async generator
index 89bb21a8674b9f1f200cc00a8f4bde2392bdda6c..acdcf579a940efd8da1c949a461a404f0694c61a 100644 (file)
@@ -1846,8 +1846,25 @@ async_gen_asend_throw(PyAsyncGenASend *o, PyObject *const *args, Py_ssize_t narg
 static PyObject *
 async_gen_asend_close(PyAsyncGenASend *o, PyObject *args)
 {
-    o->ags_state = AWAITABLE_STATE_CLOSED;
-    Py_RETURN_NONE;
+    PyObject *result;
+    if (o->ags_state == AWAITABLE_STATE_CLOSED) {
+        Py_RETURN_NONE;
+    }
+    result = async_gen_asend_throw(o, &PyExc_GeneratorExit, 1);
+    if (result == NULL) {
+        if (PyErr_ExceptionMatches(PyExc_StopIteration) ||
+            PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
+            PyErr_ExceptionMatches(PyExc_GeneratorExit))
+        {
+            PyErr_Clear();
+            Py_RETURN_NONE;
+        }
+        return result;
+    } else {
+        Py_DECREF(result);
+        PyErr_SetString(PyExc_RuntimeError, "coroutine ignored GeneratorExit");
+        return NULL;
+    }
 }
 
 static void
@@ -2291,8 +2308,25 @@ async_gen_athrow_iternext(PyAsyncGenAThrow *o)
 static PyObject *
 async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args)
 {
-    o->agt_state = AWAITABLE_STATE_CLOSED;
-    Py_RETURN_NONE;
+    PyObject *result;
+    if (o->agt_state == AWAITABLE_STATE_CLOSED) {
+        Py_RETURN_NONE;
+    }
+    result = async_gen_athrow_throw(o, &PyExc_GeneratorExit, 1);
+    if (result == NULL) {
+        if (PyErr_ExceptionMatches(PyExc_StopIteration) ||
+            PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
+            PyErr_ExceptionMatches(PyExc_GeneratorExit))
+        {
+            PyErr_Clear();
+            Py_RETURN_NONE;
+        }
+        return result;
+    } else {
+        Py_DECREF(result);
+        PyErr_SetString(PyExc_RuntimeError, "coroutine ignored GeneratorExit");
+        return NULL;
+    }
 }