summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_global_objects_fini_generated.h3
-rw-r--r--Include/internal/pycore_global_strings.h3
-rw-r--r--Include/internal/pycore_runtime_init_generated.h3
-rw-r--r--Include/internal/pycore_unicodeobject_generated.h9
-rw-r--r--Include/internal/pycore_warnings.h1
-rw-r--r--Lib/test/test_asyncgen.py38
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst1
-rw-r--r--Objects/genobject.c26
-rw-r--r--Python/_warnings.c14
9 files changed, 96 insertions, 2 deletions
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index 5a1993e..a83f8fc 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -781,6 +781,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(a));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(abs_tol));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(access));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(aclose));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add_done_callback));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(after_in_child));
@@ -794,7 +795,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(arguments));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argv));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(as_integer_ratio));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(asend));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ast));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(athrow));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(attribute));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(authorizer_callback));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(autocommit));
diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index 6196787..dd6a62f 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -269,6 +269,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(a)
STRUCT_FOR_ID(abs_tol)
STRUCT_FOR_ID(access)
+ STRUCT_FOR_ID(aclose)
STRUCT_FOR_ID(add)
STRUCT_FOR_ID(add_done_callback)
STRUCT_FOR_ID(after_in_child)
@@ -282,7 +283,9 @@ struct _Py_global_strings {
STRUCT_FOR_ID(arguments)
STRUCT_FOR_ID(argv)
STRUCT_FOR_ID(as_integer_ratio)
+ STRUCT_FOR_ID(asend)
STRUCT_FOR_ID(ast)
+ STRUCT_FOR_ID(athrow)
STRUCT_FOR_ID(attribute)
STRUCT_FOR_ID(authorizer_callback)
STRUCT_FOR_ID(autocommit)
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index 59ec49a..d689f71 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -775,6 +775,7 @@ extern "C" {
INIT_ID(a), \
INIT_ID(abs_tol), \
INIT_ID(access), \
+ INIT_ID(aclose), \
INIT_ID(add), \
INIT_ID(add_done_callback), \
INIT_ID(after_in_child), \
@@ -788,7 +789,9 @@ extern "C" {
INIT_ID(arguments), \
INIT_ID(argv), \
INIT_ID(as_integer_ratio), \
+ INIT_ID(asend), \
INIT_ID(ast), \
+ INIT_ID(athrow), \
INIT_ID(attribute), \
INIT_ID(authorizer_callback), \
INIT_ID(autocommit), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index 8f8a067..db6a157 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -648,6 +648,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(access);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(aclose);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(add);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
@@ -687,9 +690,15 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(as_integer_ratio);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(asend);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(ast);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(athrow);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(attribute);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
diff --git a/Include/internal/pycore_warnings.h b/Include/internal/pycore_warnings.h
index efb4f1c..452d6b9 100644
--- a/Include/internal/pycore_warnings.h
+++ b/Include/internal/pycore_warnings.h
@@ -22,6 +22,7 @@ extern int _PyWarnings_InitState(PyInterpreterState *interp);
PyAPI_FUNC(PyObject*) _PyWarnings_Init(void);
extern void _PyErr_WarnUnawaitedCoroutine(PyObject *coro);
+extern void _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method);
#ifdef __cplusplus
}
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index 09e4010..4f00558 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -415,8 +415,9 @@ class AsyncGenTest(unittest.TestCase):
self.assertIsInstance(g.ag_frame, types.FrameType)
self.assertFalse(g.ag_running)
self.assertIsInstance(g.ag_code, types.CodeType)
-
- self.assertTrue(inspect.isawaitable(g.aclose()))
+ aclose = g.aclose()
+ self.assertTrue(inspect.isawaitable(aclose))
+ aclose.close()
class AsyncGenAsyncioTest(unittest.TestCase):
@@ -1693,5 +1694,38 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
+class TestUnawaitedWarnings(unittest.TestCase):
+ def test_asend(self):
+ async def gen():
+ yield 1
+
+ msg = f"coroutine method 'asend' of '{gen.__qualname__}' was never awaited"
+ with self.assertWarnsRegex(RuntimeWarning, msg):
+ g = gen()
+ g.asend(None)
+ gc_collect()
+
+ def test_athrow(self):
+ async def gen():
+ yield 1
+
+ msg = f"coroutine method 'athrow' of '{gen.__qualname__}' was never awaited"
+ with self.assertWarnsRegex(RuntimeWarning, msg):
+ g = gen()
+ g.athrow(RuntimeError)
+ gc_collect()
+
+ def test_aclose(self):
+ async def gen():
+ yield 1
+
+ msg = f"coroutine method 'aclose' of '{gen.__qualname__}' was never awaited"
+ with self.assertWarnsRegex(RuntimeWarning, msg):
+ g = gen()
+ g.aclose()
+ gc_collect()
+
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst
new file mode 100644
index 0000000..084ea70
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-18-12-48-39.gh-issue-89091.FDzRcW.rst
@@ -0,0 +1 @@
+Raise :exc:`RuntimeWarning` for unawaited async generator methods like :meth:`~agen.asend`, :meth:`~agen.athrow` and :meth:`~agen.aclose`. Patch by Kumar Aditya.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 1abfc83..b40cf41 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1735,6 +1735,10 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
static void
async_gen_asend_dealloc(PyAsyncGenASend *o)
{
+ if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
+ return;
+ }
+
_PyObject_GC_UNTRACK((PyObject *)o);
Py_CLEAR(o->ags_gen);
Py_CLEAR(o->ags_sendval);
@@ -1839,6 +1843,13 @@ async_gen_asend_close(PyAsyncGenASend *o, PyObject *args)
Py_RETURN_NONE;
}
+static void
+async_gen_asend_finalize(PyAsyncGenASend *o)
+{
+ if (o->ags_state == AWAITABLE_STATE_INIT) {
+ _PyErr_WarnUnawaitedAgenMethod(o->ags_gen, &_Py_ID(asend));
+ }
+}
static PyMethodDef async_gen_asend_methods[] = {
{"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc},
@@ -1896,6 +1907,7 @@ PyTypeObject _PyAsyncGenASend_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ .tp_finalize = (destructor)async_gen_asend_finalize,
};
@@ -2053,6 +2065,10 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val)
static void
async_gen_athrow_dealloc(PyAsyncGenAThrow *o)
{
+ if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
+ return;
+ }
+
_PyObject_GC_UNTRACK((PyObject *)o);
Py_CLEAR(o->agt_gen);
Py_CLEAR(o->agt_args);
@@ -2256,6 +2272,15 @@ async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args)
}
+static void
+async_gen_athrow_finalize(PyAsyncGenAThrow *o)
+{
+ if (o->agt_state == AWAITABLE_STATE_INIT) {
+ PyObject *method = o->agt_args ? &_Py_ID(athrow) : &_Py_ID(aclose);
+ _PyErr_WarnUnawaitedAgenMethod(o->agt_gen, method);
+ }
+}
+
static PyMethodDef async_gen_athrow_methods[] = {
{"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc},
{"throw", _PyCFunction_CAST(async_gen_athrow_throw),
@@ -2313,6 +2338,7 @@ PyTypeObject _PyAsyncGenAThrow_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ .tp_finalize = (destructor)async_gen_athrow_finalize,
};
diff --git a/Python/_warnings.c b/Python/_warnings.c
index dec6586..54fa5c5 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -1366,6 +1366,20 @@ exit:
}
void
+_PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method)
+{
+ PyObject *exc = PyErr_GetRaisedException();
+ if (_PyErr_WarnFormat((PyObject *)agen, PyExc_RuntimeWarning, 1,
+ "coroutine method %R of %R was never awaited",
+ method, agen->ag_qualname) < 0)
+ {
+ PyErr_WriteUnraisable((PyObject *)agen);
+ }
+ PyErr_SetRaisedException(exc);
+}
+
+
+void
_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
{
/* First, we attempt to funnel the warning through