Skip to content

Commit c8d30fe

Browse files
committed
Fix issue #1689458 by teaching frame_setlineno how to jump to the first line of
a code object.
1 parent cf4ad76 commit c8d30fe

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

Lib/test/test_trace.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,27 @@ def test_18_no_jump_to_non_integers(self):
740740
def test_19_no_jump_without_trace_function(self):
741741
no_jump_without_trace_function()
742742

743+
def test_jump_to_firstlineno(self):
744+
# This tests that PDB can jump back to the first line in a
745+
# file. See issue #1689458. It can only be triggered in a
746+
# function call if the function is defined on a single line.
747+
code = compile("""
748+
# Comments don't count.
749+
output.append(2) # firstlineno is here.
750+
output.append(3)
751+
output.append(4)
752+
""", "<fake module>", "exec")
753+
class fake_function:
754+
func_code = code
755+
jump = (2, 0)
756+
tracer = JumpTracer(fake_function)
757+
sys.settrace(tracer.trace)
758+
namespace = {"output": []}
759+
exec code in namespace
760+
sys.settrace(None)
761+
self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"])
762+
763+
743764
def test_main():
744765
test_support.run_unittest(
745766
TraceTestCase,

Objects/frameobject.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,20 +140,26 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
140140
new_lineno);
141141
return -1;
142142
}
143-
144-
/* Find the bytecode offset for the start of the given line, or the
145-
* first code-owning line after it. */
146-
PyString_AsStringAndSize(f->f_code->co_lnotab, &lnotab, &lnotab_len);
147-
addr = 0;
148-
line = f->f_code->co_firstlineno;
149-
new_lasti = -1;
150-
for (offset = 0; offset < lnotab_len; offset += 2) {
151-
addr += lnotab[offset];
152-
line += lnotab[offset+1];
153-
if (line >= new_lineno) {
154-
new_lasti = addr;
155-
new_lineno = line;
156-
break;
143+
else if (new_lineno == f->f_code->co_firstlineno) {
144+
new_lasti = 0;
145+
new_lineno = f->f_code->co_firstlineno;
146+
}
147+
else {
148+
/* Find the bytecode offset for the start of the given
149+
* line, or the first code-owning line after it. */
150+
PyString_AsStringAndSize(f->f_code->co_lnotab,
151+
&lnotab, &lnotab_len);
152+
addr = 0;
153+
line = f->f_code->co_firstlineno;
154+
new_lasti = -1;
155+
for (offset = 0; offset < lnotab_len; offset += 2) {
156+
addr += lnotab[offset];
157+
line += lnotab[offset+1];
158+
if (line >= new_lineno) {
159+
new_lasti = addr;
160+
new_lineno = line;
161+
break;
162+
}
157163
}
158164
}
159165

0 commit comments

Comments
 (0)