Skip to content

Commit ee9f98d

Browse files
authored
bpo-42823: Fix frame lineno when frame.f_trace is set (GH-24099)
* Add test for frame.f_lineno with/without tracing. * Make sure that frame.f_lineno is correct regardless of whether frame.f_trace is set. * Update importlib * Add NEWS
1 parent e40e2a2 commit ee9f98d

File tree

8 files changed

+1181
-1158
lines changed

8 files changed

+1181
-1158
lines changed

Include/cpython/frameobject.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,7 @@ struct _frame {
4242
PyObject *f_gen;
4343

4444
int f_lasti; /* Last instruction if called */
45-
/* Call PyFrame_GetLineNumber() instead of reading this field
46-
directly. As of 2.3 f_lineno is only valid when tracing is
47-
active (i.e. when f_trace is set). At other times we use
48-
PyCode_Addr2Line to calculate the line from the current
49-
bytecode index. */
50-
int f_lineno; /* Current line number */
45+
int f_lineno; /* Current line number. Only valid if non-zero */
5146
int f_iblock; /* index in f_blockstack */
5247
PyFrameState f_state; /* What state the frame is in */
5348
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */

Lib/test/test_frame.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import re
2+
import sys
23
import types
34
import unittest
45
import weakref
@@ -94,6 +95,26 @@ def g():
9495
f.clear()
9596
self.assertTrue(endly)
9697

98+
def test_lineno_with_tracing(self):
99+
def record_line():
100+
f = sys._getframe(1)
101+
lines.append(f.f_lineno-f.f_code.co_firstlineno)
102+
103+
def test(trace):
104+
record_line()
105+
if trace:
106+
sys._getframe(0).f_trace = True
107+
record_line()
108+
record_line()
109+
110+
expected_lines = [1, 4, 5]
111+
lines = []
112+
test(False)
113+
self.assertEqual(lines, expected_lines)
114+
lines = []
115+
test(True)
116+
self.assertEqual(lines, expected_lines)
117+
97118
@support.cpython_only
98119
def test_clear_refcycles(self):
99120
# .clear() doesn't leave any refcycle behind
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
frame.f_lineno is correct even if frame.f_trace is set to True

Objects/codeobject.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,8 @@ PyLineTable_InitAddressRange(char *linetable, int firstlineno, PyCodeAddressRang
12611261
range->lo_next = linetable;
12621262
range->ar_start = -1;
12631263
range->ar_end = 0;
1264-
range->ar_computed_line = range->ar_line = firstlineno;
1264+
range->ar_computed_line = firstlineno;
1265+
range->ar_line = -1;
12651266
}
12661267

12671268
int

Objects/frameobject.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ int
4444
PyFrame_GetLineNumber(PyFrameObject *f)
4545
{
4646
assert(f != NULL);
47-
if (f->f_trace) {
47+
if (f->f_lineno != 0) {
4848
return f->f_lineno;
4949
}
5050
else {
@@ -476,8 +476,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
476476
start_block_stack = pop_block(start_block_stack);
477477
}
478478

479-
/* Finally set the new f_lineno and f_lasti and return OK. */
480-
f->f_lineno = new_lineno;
479+
/* Finally set the new f_lasti and return OK. */
480+
f->f_lineno = 0;
481481
f->f_lasti = best_addr;
482482
return 0;
483483
}
@@ -498,11 +498,9 @@ frame_gettrace(PyFrameObject *f, void *closure)
498498
static int
499499
frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
500500
{
501-
/* We rely on f_lineno being accurate when f_trace is set. */
502-
f->f_lineno = PyFrame_GetLineNumber(f);
503-
504-
if (v == Py_None)
501+
if (v == Py_None) {
505502
v = NULL;
503+
}
506504
Py_XINCREF(v);
507505
Py_XSETREF(f->f_trace, v);
508506

Python/ceval.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4993,27 +4993,28 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
49934993
PyCodeAddressRange *bounds, int *instr_prev)
49944994
{
49954995
int result = 0;
4996-
int line = frame->f_lineno;
49974996

4998-
/* If the last instruction executed isn't in the current
4999-
instruction window, reset the window.
5000-
*/
5001-
line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
50024997
/* If the last instruction falls at the start of a line or if it
50034998
represents a jump backwards, update the frame's line number and
50044999
then call the trace function if we're tracing source lines.
50055000
*/
5006-
if ((line != frame->f_lineno || frame->f_lasti < *instr_prev)) {
5007-
if (line != -1) {
5001+
int lastline = bounds->ar_line;
5002+
int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
5003+
if (line != -1 && frame->f_trace_lines) {
5004+
/* Trace backward edges or first instruction of a new line */
5005+
if (frame->f_lasti < *instr_prev ||
5006+
(line != lastline && frame->f_lasti == bounds->ar_start))
5007+
{
50085008
frame->f_lineno = line;
5009-
if (frame->f_trace_lines) {
5010-
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
5011-
}
5009+
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
5010+
frame->f_lineno = 0;
50125011
}
50135012
}
50145013
/* Always emit an opcode event if we're tracing all opcodes. */
50155014
if (frame->f_trace_opcodes) {
5015+
frame->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
50165016
result = call_trace(func, obj, tstate, frame, PyTrace_OPCODE, Py_None);
5017+
frame->f_lineno = 0;
50175018
}
50185019
*instr_prev = frame->f_lasti;
50195020
return result;

Python/compile.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6558,6 +6558,12 @@ ensure_exits_have_lineno(struct compiler *c)
65586558
if (is_exit_without_lineno(entry)) {
65596559
entry->b_instr[0].i_lineno = c->u->u_firstlineno;
65606560
}
6561+
/* Eliminate empty blocks */
6562+
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
6563+
while (b->b_next && b->b_next->b_iused == 0) {
6564+
b->b_next = b->b_next->b_next;
6565+
}
6566+
}
65616567
/* Any remaining reachable exit blocks without line number can only be reached by
65626568
* fall through, and thus can only have a single predecessor */
65636569
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {

0 commit comments

Comments
 (0)