bpo-42823: Fix frame lineno when frame.f_trace is set by markshannon · Pull Request #24099 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions Include/cpython/frameobject.h
21 changes: 21 additions & 0 deletions Lib/test/test_frame.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import sys
import types
import unittest
import weakref
Expand Down Expand Up @@ -94,6 +95,26 @@ def g():
f.clear()
self.assertTrue(endly)

def test_lineno_with_tracing(self):
def record_line():
f = sys._getframe(1)
lines.append(f.f_lineno-f.f_code.co_firstlineno)

def test(trace):
record_line()
if trace:
sys._getframe(0).f_trace = True
record_line()
record_line()

expected_lines = [1, 4, 5]
lines = []
test(False)
self.assertEqual(lines, expected_lines)
lines = []
test(True)
self.assertEqual(lines, expected_lines)

@support.cpython_only
def test_clear_refcycles(self):
# .clear() doesn't leave any refcycle behind
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
frame.f_lineno is correct even if frame.f_trace is set to True
3 changes: 2 additions & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,8 @@ PyLineTable_InitAddressRange(char *linetable, int firstlineno, PyCodeAddressRang
range->lo_next = linetable;
range->ar_start = -1;
range->ar_end = 0;
range->ar_computed_line = range->ar_line = firstlineno;
range->ar_computed_line = firstlineno;
range->ar_line = -1;
}

int
Expand Down
12 changes: 5 additions & 7 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ int
PyFrame_GetLineNumber(PyFrameObject *f)
{
assert(f != NULL);
if (f->f_trace) {
if (f->f_lineno != 0) {
return f->f_lineno;
}
else {
Expand Down Expand Up @@ -476,8 +476,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
start_block_stack = pop_block(start_block_stack);
}

/* Finally set the new f_lineno and f_lasti and return OK. */
f->f_lineno = new_lineno;
/* Finally set the new f_lasti and return OK. */
f->f_lineno = 0;
f->f_lasti = best_addr;
return 0;
}
Expand All @@ -498,11 +498,9 @@ frame_gettrace(PyFrameObject *f, void *closure)
static int
frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
{
/* We rely on f_lineno being accurate when f_trace is set. */
f->f_lineno = PyFrame_GetLineNumber(f);

if (v == Py_None)
if (v == Py_None) {
v = NULL;
}
Py_XINCREF(v);
Py_XSETREF(f->f_trace, v);

Expand Down
21 changes: 11 additions & 10 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -4993,27 +4993,28 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyCodeAddressRange *bounds, int *instr_prev)
{
int result = 0;
int line = frame->f_lineno;

/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
/* If the last instruction falls at the start of a line or if it
represents a jump backwards, update the frame's line number and
then call the trace function if we're tracing source lines.
*/
if ((line != frame->f_lineno || frame->f_lasti < *instr_prev)) {
if (line != -1) {
int lastline = bounds->ar_line;
int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
if (line != -1 && frame->f_trace_lines) {
/* Trace backward edges or first instruction of a new line */
if (frame->f_lasti < *instr_prev ||
(line != lastline && frame->f_lasti == bounds->ar_start))
{
frame->f_lineno = line;
if (frame->f_trace_lines) {
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
}
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
frame->f_lineno = 0;
}
}
/* Always emit an opcode event if we're tracing all opcodes. */
if (frame->f_trace_opcodes) {
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None);
frame->f_lineno = 0;
}
*instr_prev = frame->f_lasti;
return result;
Expand Down
6 changes: 6 additions & 0 deletions Python/compile.c
Loading