mirror of https://github.com/python/cpython
GH-89091: raise `RuntimeWarning` for unawaited async generator methods (#104611)
This commit is contained in:
parent
46857d0b2a
commit
7fc542c88d
|
@ -781,6 +781,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(a));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(a));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(abs_tol));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(abs_tol));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(access));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(access));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(aclose));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add_done_callback));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(add_done_callback));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(after_in_child));
|
_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(arguments));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argv));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(argv));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(as_integer_ratio));
|
_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(ast));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(athrow));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(attribute));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(attribute));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(authorizer_callback));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(authorizer_callback));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(autocommit));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(autocommit));
|
||||||
|
|
|
@ -269,6 +269,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(a)
|
STRUCT_FOR_ID(a)
|
||||||
STRUCT_FOR_ID(abs_tol)
|
STRUCT_FOR_ID(abs_tol)
|
||||||
STRUCT_FOR_ID(access)
|
STRUCT_FOR_ID(access)
|
||||||
|
STRUCT_FOR_ID(aclose)
|
||||||
STRUCT_FOR_ID(add)
|
STRUCT_FOR_ID(add)
|
||||||
STRUCT_FOR_ID(add_done_callback)
|
STRUCT_FOR_ID(add_done_callback)
|
||||||
STRUCT_FOR_ID(after_in_child)
|
STRUCT_FOR_ID(after_in_child)
|
||||||
|
@ -282,7 +283,9 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(arguments)
|
STRUCT_FOR_ID(arguments)
|
||||||
STRUCT_FOR_ID(argv)
|
STRUCT_FOR_ID(argv)
|
||||||
STRUCT_FOR_ID(as_integer_ratio)
|
STRUCT_FOR_ID(as_integer_ratio)
|
||||||
|
STRUCT_FOR_ID(asend)
|
||||||
STRUCT_FOR_ID(ast)
|
STRUCT_FOR_ID(ast)
|
||||||
|
STRUCT_FOR_ID(athrow)
|
||||||
STRUCT_FOR_ID(attribute)
|
STRUCT_FOR_ID(attribute)
|
||||||
STRUCT_FOR_ID(authorizer_callback)
|
STRUCT_FOR_ID(authorizer_callback)
|
||||||
STRUCT_FOR_ID(autocommit)
|
STRUCT_FOR_ID(autocommit)
|
||||||
|
|
|
@ -775,6 +775,7 @@ extern "C" {
|
||||||
INIT_ID(a), \
|
INIT_ID(a), \
|
||||||
INIT_ID(abs_tol), \
|
INIT_ID(abs_tol), \
|
||||||
INIT_ID(access), \
|
INIT_ID(access), \
|
||||||
|
INIT_ID(aclose), \
|
||||||
INIT_ID(add), \
|
INIT_ID(add), \
|
||||||
INIT_ID(add_done_callback), \
|
INIT_ID(add_done_callback), \
|
||||||
INIT_ID(after_in_child), \
|
INIT_ID(after_in_child), \
|
||||||
|
@ -788,7 +789,9 @@ extern "C" {
|
||||||
INIT_ID(arguments), \
|
INIT_ID(arguments), \
|
||||||
INIT_ID(argv), \
|
INIT_ID(argv), \
|
||||||
INIT_ID(as_integer_ratio), \
|
INIT_ID(as_integer_ratio), \
|
||||||
|
INIT_ID(asend), \
|
||||||
INIT_ID(ast), \
|
INIT_ID(ast), \
|
||||||
|
INIT_ID(athrow), \
|
||||||
INIT_ID(attribute), \
|
INIT_ID(attribute), \
|
||||||
INIT_ID(authorizer_callback), \
|
INIT_ID(authorizer_callback), \
|
||||||
INIT_ID(autocommit), \
|
INIT_ID(autocommit), \
|
||||||
|
|
|
@ -648,6 +648,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(access);
|
string = &_Py_ID(access);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(aclose);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(add);
|
string = &_Py_ID(add);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
@ -687,9 +690,15 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(as_integer_ratio);
|
string = &_Py_ID(as_integer_ratio);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(asend);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(ast);
|
string = &_Py_ID(ast);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(athrow);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(attribute);
|
string = &_Py_ID(attribute);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -22,6 +22,7 @@ extern int _PyWarnings_InitState(PyInterpreterState *interp);
|
||||||
PyAPI_FUNC(PyObject*) _PyWarnings_Init(void);
|
PyAPI_FUNC(PyObject*) _PyWarnings_Init(void);
|
||||||
|
|
||||||
extern void _PyErr_WarnUnawaitedCoroutine(PyObject *coro);
|
extern void _PyErr_WarnUnawaitedCoroutine(PyObject *coro);
|
||||||
|
extern void _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,8 +415,9 @@ class AsyncGenTest(unittest.TestCase):
|
||||||
self.assertIsInstance(g.ag_frame, types.FrameType)
|
self.assertIsInstance(g.ag_frame, types.FrameType)
|
||||||
self.assertFalse(g.ag_running)
|
self.assertFalse(g.ag_running)
|
||||||
self.assertIsInstance(g.ag_code, types.CodeType)
|
self.assertIsInstance(g.ag_code, types.CodeType)
|
||||||
|
aclose = g.aclose()
|
||||||
self.assertTrue(inspect.isawaitable(g.aclose()))
|
self.assertTrue(inspect.isawaitable(aclose))
|
||||||
|
aclose.close()
|
||||||
|
|
||||||
|
|
||||||
class AsyncGenAsyncioTest(unittest.TestCase):
|
class AsyncGenAsyncioTest(unittest.TestCase):
|
||||||
|
@ -1693,5 +1694,38 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||||
self.loop.run_until_complete(run())
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -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.
|
|
@ -1735,6 +1735,10 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
|
||||||
static void
|
static void
|
||||||
async_gen_asend_dealloc(PyAsyncGenASend *o)
|
async_gen_asend_dealloc(PyAsyncGenASend *o)
|
||||||
{
|
{
|
||||||
|
if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_PyObject_GC_UNTRACK((PyObject *)o);
|
_PyObject_GC_UNTRACK((PyObject *)o);
|
||||||
Py_CLEAR(o->ags_gen);
|
Py_CLEAR(o->ags_gen);
|
||||||
Py_CLEAR(o->ags_sendval);
|
Py_CLEAR(o->ags_sendval);
|
||||||
|
@ -1839,6 +1843,13 @@ async_gen_asend_close(PyAsyncGenASend *o, PyObject *args)
|
||||||
Py_RETURN_NONE;
|
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[] = {
|
static PyMethodDef async_gen_asend_methods[] = {
|
||||||
{"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc},
|
{"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc},
|
||||||
|
@ -1896,6 +1907,7 @@ PyTypeObject _PyAsyncGenASend_Type = {
|
||||||
0, /* tp_init */
|
0, /* tp_init */
|
||||||
0, /* tp_alloc */
|
0, /* tp_alloc */
|
||||||
0, /* tp_new */
|
0, /* tp_new */
|
||||||
|
.tp_finalize = (destructor)async_gen_asend_finalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2053,6 +2065,10 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val)
|
||||||
static void
|
static void
|
||||||
async_gen_athrow_dealloc(PyAsyncGenAThrow *o)
|
async_gen_athrow_dealloc(PyAsyncGenAThrow *o)
|
||||||
{
|
{
|
||||||
|
if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_PyObject_GC_UNTRACK((PyObject *)o);
|
_PyObject_GC_UNTRACK((PyObject *)o);
|
||||||
Py_CLEAR(o->agt_gen);
|
Py_CLEAR(o->agt_gen);
|
||||||
Py_CLEAR(o->agt_args);
|
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[] = {
|
static PyMethodDef async_gen_athrow_methods[] = {
|
||||||
{"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc},
|
{"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc},
|
||||||
{"throw", _PyCFunction_CAST(async_gen_athrow_throw),
|
{"throw", _PyCFunction_CAST(async_gen_athrow_throw),
|
||||||
|
@ -2313,6 +2338,7 @@ PyTypeObject _PyAsyncGenAThrow_Type = {
|
||||||
0, /* tp_init */
|
0, /* tp_init */
|
||||||
0, /* tp_alloc */
|
0, /* tp_alloc */
|
||||||
0, /* tp_new */
|
0, /* tp_new */
|
||||||
|
.tp_finalize = (destructor)async_gen_athrow_finalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1365,6 +1365,20 @@ exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
|
_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue