summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorNathaniel J. Smith <njs@pobox.com>2018-01-21 14:44:07 (GMT)
committerYury Selivanov <yury@magic.io>2018-01-21 14:44:07 (GMT)
commitfc2f407829d9817ddacccae6944dd0879cfaca24 (patch)
tree1775a28a8181975363798f9b3e7cb2bb100e49a2 /Objects
parent1211c9a9897a174b7261ca258cabf289815a40d8 (diff)
downloadcpython-fc2f407829d9817ddacccae6944dd0879cfaca24.zip
cpython-fc2f407829d9817ddacccae6944dd0879cfaca24.tar.gz
cpython-fc2f407829d9817ddacccae6944dd0879cfaca24.tar.bz2
bpo-32591: Add native coroutine origin tracking (#5250)
* Add coro.cr_origin and sys.set_coroutine_origin_tracking_depth * Use coroutine origin information in the unawaited coroutine warning * Stop using set_coroutine_wrapper in asyncio debug mode * In BaseEventLoop.set_debug, enable debugging in the correct thread
Diffstat (limited to 'Objects')
-rw-r--r--Objects/genobject.c61
1 files changed, 57 insertions, 4 deletions
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 00a8823..0a34c1f 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -32,6 +32,8 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
Py_VISIT(gen->gi_code);
Py_VISIT(gen->gi_name);
Py_VISIT(gen->gi_qualname);
+ /* No need to visit cr_origin, because it's just tuples/str/int, so can't
+ participate in a reference cycle. */
return exc_state_traverse(&gen->gi_exc_state, visit, arg);
}
@@ -75,9 +77,7 @@ _PyGen_Finalize(PyObject *self)
((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
gen->gi_frame->f_lasti == -1) {
if (!error_value) {
- PyErr_WarnFormat(PyExc_RuntimeWarning, 1,
- "coroutine '%.50S' was never awaited",
- gen->gi_qualname);
+ _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
}
}
else {
@@ -137,6 +137,9 @@ gen_dealloc(PyGenObject *gen)
gen->gi_frame->f_gen = NULL;
Py_CLEAR(gen->gi_frame);
}
+ if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) {
+ Py_CLEAR(((PyCoroObject *)gen)->cr_origin);
+ }
Py_CLEAR(gen->gi_code);
Py_CLEAR(gen->gi_name);
Py_CLEAR(gen->gi_qualname);
@@ -990,6 +993,7 @@ static PyMemberDef coro_memberlist[] = {
{"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY},
{"cr_running", T_BOOL, offsetof(PyCoroObject, cr_running), READONLY},
{"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY},
+ {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
{NULL} /* Sentinel */
};
@@ -1158,10 +1162,59 @@ PyTypeObject _PyCoroWrapper_Type = {
0, /* tp_free */
};
+static PyObject *
+compute_cr_origin(int origin_depth)
+{
+ PyFrameObject *frame = PyEval_GetFrame();
+ /* First count how many frames we have */
+ int frame_count = 0;
+ for (; frame && frame_count < origin_depth; ++frame_count) {
+ frame = frame->f_back;
+ }
+
+ /* Now collect them */
+ PyObject *cr_origin = PyTuple_New(frame_count);
+ frame = PyEval_GetFrame();
+ for (int i = 0; i < frame_count; ++i) {
+ PyObject *frameinfo = Py_BuildValue(
+ "OiO",
+ frame->f_code->co_filename,
+ PyFrame_GetLineNumber(frame),
+ frame->f_code->co_name);
+ if (!frameinfo) {
+ Py_DECREF(cr_origin);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(cr_origin, i, frameinfo);
+ frame = frame->f_back;
+ }
+
+ return cr_origin;
+}
+
PyObject *
PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
{
- return gen_new_with_qualname(&PyCoro_Type, f, name, qualname);
+ PyObject *coro = gen_new_with_qualname(&PyCoro_Type, f, name, qualname);
+ if (!coro) {
+ return NULL;
+ }
+
+ PyThreadState *tstate = PyThreadState_GET();
+ int origin_depth = tstate->coroutine_origin_tracking_depth;
+
+ if (origin_depth == 0) {
+ ((PyCoroObject *)coro)->cr_origin = NULL;
+ } else {
+ PyObject *cr_origin = compute_cr_origin(origin_depth);
+ if (!cr_origin) {
+ Py_DECREF(coro);
+ return NULL;
+ }
+ ((PyCoroObject *)coro)->cr_origin = cr_origin;
+ }
+
+ return coro;
}