summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_sys_settrace.py53
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-05-12-13-23-19.gh-issue-92236.sDRzUe.rst2
-rw-r--r--Modules/_testcapimodule.c46
-rw-r--r--Python/ceval.c6
4 files changed, 107 insertions, 0 deletions
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index 715204e..9cc6bcf 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -7,6 +7,7 @@ import difflib
import gc
from functools import wraps
import asyncio
+from test.support import import_helper
support.requires_working_socket(module=True)
@@ -1473,6 +1474,58 @@ class TraceTestCase(unittest.TestCase):
(3, '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):
"""Repeat the trace tests, but with per-line events skipped"""
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-05-12-13-23-19.gh-issue-92236.sDRzUe.rst b/Misc/NEWS.d/next/Core and Builtins/2022-05-12-13-23-19.gh-issue-92236.sDRzUe.rst
new file mode 100644
index 0000000..fe482d5
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-05-12-13-23-19.gh-issue-92236.sDRzUe.rst
@@ -0,0 +1,2 @@
+Remove spurious "LINE" event when starting a generator or coroutine, visible
+tracing functions implemented in C.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 4371bf7..363dbbb 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -5787,6 +5787,51 @@ test_code_api(PyObject *self, PyObject *Py_UNUSED(args))
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 *test_buildvalue_issue38913(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},
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
{"test_code_api", test_code_api, METH_NOARGS, NULL},
+ {"settrace_to_record", settrace_to_record, METH_O, NULL},
{NULL, NULL} /* sentinel */
};
diff --git a/Python/ceval.c b/Python/ceval.c
index f6b07b4..c73218f 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -5680,6 +5680,12 @@ handle_eval_breaker:
TRACE_FUNCTION_ENTRY();
DTRACE_FUNCTION_ENTRY();
break;
+ case POP_TOP:
+ if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) {
+ /* Frame not fully initialized */
+ break;
+ }
+ /* fall through */
default:
/* line-by-line tracing support */
if (PyDTrace_LINE_ENABLED()) {