Skip to content

Commit 22a1db3

Browse files
authored
pythonGH-92236: Remove spurious "line" event when starting coroutine or generator. (pythonGH-92722)
1 parent db388df commit 22a1db3

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

Lib/test/test_sys_settrace.py

+53
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import gc
88
from functools import wraps
99
import asyncio
10+
from test.support import import_helper
1011

1112
support.requires_working_socket(module=True)
1213

@@ -1473,6 +1474,58 @@ def __init__(self):
14731474
(3, 'return'),
14741475
(1, 'return')])
14751476

1477+
@support.cpython_only
1478+
def test_no_line_event_after_creating_generator(self):
1479+
# Spurious line events before call events only show up with C tracer
1480+
1481+
# Skip this test if the _testcapi module isn't available.
1482+
_testcapi = import_helper.import_module('_testcapi')
1483+
1484+
def gen():
1485+
yield 1
1486+
1487+
def func():
1488+
for _ in (
1489+
gen()
1490+
):
1491+
pass
1492+
1493+
EXPECTED_EVENTS = [
1494+
(0, 'call'),
1495+
(2, 'line'),
1496+
(1, 'line'),
1497+
(-3, 'call'),
1498+
(-2, 'line'),
1499+
(-2, 'return'),
1500+
(4, 'line'),
1501+
(1, 'line'),
1502+
(-2, 'call'),
1503+
(-2, 'return'),
1504+
(1, 'return'),
1505+
]
1506+
1507+
# C level events should be the same as expected and the same as Python level.
1508+
1509+
events = []
1510+
# Turning on and off tracing must be on same line to avoid unwanted LINE events.
1511+
_testcapi.settrace_to_record(events); func(); sys.settrace(None)
1512+
start_line = func.__code__.co_firstlineno
1513+
events = [
1514+
(line-start_line, EVENT_NAMES[what])
1515+
for (what, line, arg) in events
1516+
]
1517+
self.assertEqual(events, EXPECTED_EVENTS)
1518+
1519+
self.run_and_compare(func, EXPECTED_EVENTS)
1520+
1521+
1522+
EVENT_NAMES = [
1523+
'call',
1524+
'exception',
1525+
'line',
1526+
'return'
1527+
]
1528+
14761529

14771530
class SkipLineEventsTraceTestCase(TraceTestCase):
14781531
"""Repeat the trace tests, but with per-line events skipped"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Remove spurious "LINE" event when starting a generator or coroutine, visible
2+
tracing functions implemented in C.

Modules/_testcapimodule.c

+46
Original file line numberDiff line numberDiff line change
@@ -5787,6 +5787,51 @@ test_code_api(PyObject *self, PyObject *Py_UNUSED(args))
57875787
Py_RETURN_NONE;
57885788
}
57895789

5790+
static int
5791+
record_func(PyObject *obj, PyFrameObject *f, int what, PyObject *arg)
5792+
{
5793+
assert(PyList_Check(obj));
5794+
PyObject *what_obj = NULL;
5795+
PyObject *line_obj = NULL;
5796+
PyObject *tuple = NULL;
5797+
int res = -1;
5798+
what_obj = PyLong_FromLong(what);
5799+
if (what_obj == NULL) {
5800+
goto error;
5801+
}
5802+
int line = PyFrame_GetLineNumber(f);
5803+
line_obj = PyLong_FromLong(line);
5804+
if (line_obj == NULL) {
5805+
goto error;
5806+
}
5807+
tuple = PyTuple_Pack(3, what_obj, line_obj, arg);
5808+
if (tuple == NULL) {
5809+
goto error;
5810+
}
5811+
PyTuple_SET_ITEM(tuple, 0, what_obj);
5812+
if (PyList_Append(obj, tuple)) {
5813+
goto error;
5814+
}
5815+
res = 0;
5816+
error:
5817+
Py_XDECREF(what_obj);
5818+
Py_XDECREF(line_obj);
5819+
Py_XDECREF(tuple);
5820+
return res;
5821+
}
5822+
5823+
static PyObject *
5824+
settrace_to_record(PyObject *self, PyObject *list)
5825+
{
5826+
5827+
if (!PyList_Check(list)) {
5828+
PyErr_SetString(PyExc_TypeError, "argument must be a list");
5829+
return NULL;
5830+
}
5831+
PyEval_SetTrace(record_func, list);
5832+
Py_RETURN_NONE;
5833+
}
5834+
57905835
static PyObject *negative_dictoffset(PyObject *, PyObject *);
57915836
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
57925837
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
@@ -6076,6 +6121,7 @@ static PyMethodDef TestMethods[] = {
60766121
{"frame_getlasti", frame_getlasti, METH_O, NULL},
60776122
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
60786123
{"test_code_api", test_code_api, METH_NOARGS, NULL},
6124+
{"settrace_to_record", settrace_to_record, METH_O, NULL},
60796125
{NULL, NULL} /* sentinel */
60806126
};
60816127

Python/ceval.c

+6
Original file line numberDiff line numberDiff line change
@@ -5680,6 +5680,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
56805680
TRACE_FUNCTION_ENTRY();
56815681
DTRACE_FUNCTION_ENTRY();
56825682
break;
5683+
case POP_TOP:
5684+
if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) {
5685+
/* Frame not fully initialized */
5686+
break;
5687+
}
5688+
/* fall through */
56835689
default:
56845690
/* line-by-line tracing support */
56855691
if (PyDTrace_LINE_ENABLED()) {

0 commit comments

Comments
 (0)