[Python-checkins] GH-92236: Remove spurious "line" event when starting coroutine or generator. (GH-92722) (GH-92772)

markshannon webhook-mailer at python.org
Fri May 13 06:52:58 EDT 2022


https://github.com/python/cpython/commit/2e8b2d0ee2f8d0c53d2747d400b83c171732d3a9
commit: 2e8b2d0ee2f8d0c53d2747d400b83c171732d3a9
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: markshannon <mark at hotpy.org>
date: 2022-05-13T11:52:54+01:00
summary:

GH-92236: Remove spurious "line" event when starting coroutine or generator. (GH-92722) (GH-92772)

(cherry picked from commit 22a1db378c5c381272362c5b2f68ac78a368e136)

files:
A Misc/NEWS.d/next/Core and Builtins/2022-05-12-13-23-19.gh-issue-92236.sDRzUe.rst
M Lib/test/test_sys_settrace.py
M Modules/_testcapimodule.c
M Python/ceval.c

diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index b1c8f6f80af83..1a67fa673dd64 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -7,6 +7,7 @@
 import gc
 from functools import wraps
 import asyncio
+from test.support import import_helper
 
 support.requires_working_socket(module=True)
 
@@ -1473,6 +1474,58 @@ def __init__(self):
              (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 0000000000000..fe482d505c67b
--- /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 be9ed50e0dbeb..de1a8e6c20b6c 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -5955,6 +5955,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*);
@@ -6251,6 +6296,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 f6b07b4b28eb6..c73218fcf307e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -5680,6 +5680,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
                     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()) {



More information about the Python-checkins mailing list