mirror of https://github.com/python/cpython
GH-92236: Remove spurious "line" event when starting coroutine or generator. (GH-92722)
This commit is contained in:
parent
db388df1d9
commit
22a1db378c
|
@ -7,6 +7,7 @@ import difflib
|
||||||
import gc
|
import gc
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from test.support import import_helper
|
||||||
|
|
||||||
support.requires_working_socket(module=True)
|
support.requires_working_socket(module=True)
|
||||||
|
|
||||||
|
@ -1473,6 +1474,58 @@ class TraceTestCase(unittest.TestCase):
|
||||||
(3, 'return'),
|
(3, 'return'),
|
||||||
(1, 'return')])
|
(1, 'return')])
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
def test_no_line_event_after_creating_generator(self):
|
||||||
|
# Spurious line events before call events only show up with C tracer
|
||||||
|
|
||||||
|
# Skip this test if the _testcapi module isn't available.
|
||||||
|
_testcapi = import_helper.import_module('_testcapi')
|
||||||
|
|
||||||
|
def gen():
|
||||||
|
yield 1
|
||||||
|
|
||||||
|
def func():
|
||||||
|
for _ in (
|
||||||
|
gen()
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
EXPECTED_EVENTS = [
|
||||||
|
(0, 'call'),
|
||||||
|
(2, 'line'),
|
||||||
|
(1, 'line'),
|
||||||
|
(-3, 'call'),
|
||||||
|
(-2, 'line'),
|
||||||
|
(-2, 'return'),
|
||||||
|
(4, 'line'),
|
||||||
|
(1, 'line'),
|
||||||
|
(-2, 'call'),
|
||||||
|
(-2, 'return'),
|
||||||
|
(1, 'return'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# C level events should be the same as expected and the same as Python level.
|
||||||
|
|
||||||
|
events = []
|
||||||
|
# Turning on and off tracing must be on same line to avoid unwanted LINE events.
|
||||||
|
_testcapi.settrace_to_record(events); func(); sys.settrace(None)
|
||||||
|
start_line = func.__code__.co_firstlineno
|
||||||
|
events = [
|
||||||
|
(line-start_line, EVENT_NAMES[what])
|
||||||
|
for (what, line, arg) in events
|
||||||
|
]
|
||||||
|
self.assertEqual(events, EXPECTED_EVENTS)
|
||||||
|
|
||||||
|
self.run_and_compare(func, EXPECTED_EVENTS)
|
||||||
|
|
||||||
|
|
||||||
|
EVENT_NAMES = [
|
||||||
|
'call',
|
||||||
|
'exception',
|
||||||
|
'line',
|
||||||
|
'return'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SkipLineEventsTraceTestCase(TraceTestCase):
|
class SkipLineEventsTraceTestCase(TraceTestCase):
|
||||||
"""Repeat the trace tests, but with per-line events skipped"""
|
"""Repeat the trace tests, but with per-line events skipped"""
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Remove spurious "LINE" event when starting a generator or coroutine, visible
|
||||||
|
tracing functions implemented in C.
|
|
@ -5787,6 +5787,51 @@ test_code_api(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
record_func(PyObject *obj, PyFrameObject *f, int what, PyObject *arg)
|
||||||
|
{
|
||||||
|
assert(PyList_Check(obj));
|
||||||
|
PyObject *what_obj = NULL;
|
||||||
|
PyObject *line_obj = NULL;
|
||||||
|
PyObject *tuple = NULL;
|
||||||
|
int res = -1;
|
||||||
|
what_obj = PyLong_FromLong(what);
|
||||||
|
if (what_obj == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
int line = PyFrame_GetLineNumber(f);
|
||||||
|
line_obj = PyLong_FromLong(line);
|
||||||
|
if (line_obj == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
tuple = PyTuple_Pack(3, what_obj, line_obj, arg);
|
||||||
|
if (tuple == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
PyTuple_SET_ITEM(tuple, 0, what_obj);
|
||||||
|
if (PyList_Append(obj, tuple)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
res = 0;
|
||||||
|
error:
|
||||||
|
Py_XDECREF(what_obj);
|
||||||
|
Py_XDECREF(line_obj);
|
||||||
|
Py_XDECREF(tuple);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
settrace_to_record(PyObject *self, PyObject *list)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!PyList_Check(list)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "argument must be a list");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyEval_SetTrace(record_func, list);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *negative_dictoffset(PyObject *, PyObject *);
|
static PyObject *negative_dictoffset(PyObject *, PyObject *);
|
||||||
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
||||||
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
|
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
|
||||||
|
@ -6076,6 +6121,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"frame_getlasti", frame_getlasti, METH_O, NULL},
|
{"frame_getlasti", frame_getlasti, METH_O, NULL},
|
||||||
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
|
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
|
||||||
{"test_code_api", test_code_api, METH_NOARGS, NULL},
|
{"test_code_api", test_code_api, METH_NOARGS, NULL},
|
||||||
|
{"settrace_to_record", settrace_to_record, METH_O, NULL},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5680,6 +5680,12 @@ handle_eval_breaker:
|
||||||
TRACE_FUNCTION_ENTRY();
|
TRACE_FUNCTION_ENTRY();
|
||||||
DTRACE_FUNCTION_ENTRY();
|
DTRACE_FUNCTION_ENTRY();
|
||||||
break;
|
break;
|
||||||
|
case POP_TOP:
|
||||||
|
if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) {
|
||||||
|
/* Frame not fully initialized */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
default:
|
default:
|
||||||
/* line-by-line tracing support */
|
/* line-by-line tracing support */
|
||||||
if (PyDTrace_LINE_ENABLED()) {
|
if (PyDTrace_LINE_ENABLED()) {
|
||||||
|
|
Loading…
Reference in New Issue