Index: Objects/frameobject.c =================================================================== --- Objects/frameobject.c (revision 72772) +++ Objects/frameobject.c (working copy) @@ -127,7 +127,8 @@ if (!f->f_trace) { PyErr_Format(PyExc_ValueError, - "f_lineno can only be set by a trace function"); + "f_lineno can only be set by a" + " line trace function"); return -1; } @@ -139,20 +140,26 @@ new_lineno); return -1; } - - /* Find the bytecode offset for the start of the given line, or the - * first code-owning line after it. */ - PyString_AsStringAndSize(f->f_code->co_lnotab, &lnotab, &lnotab_len); - addr = 0; - line = f->f_code->co_firstlineno; - new_lasti = -1; - for (offset = 0; offset < lnotab_len; offset += 2) { - addr += lnotab[offset]; - line += lnotab[offset+1]; - if (line >= new_lineno) { - new_lasti = addr; - new_lineno = line; - break; + else if (new_lineno == f->f_code->co_firstlineno) { + new_lasti = 0; + new_lineno = f->f_code->co_firstlineno; + } + else { + /* Find the bytecode offset for the start of the given + * line, or the first code-owning line after it. */ + PyString_AsStringAndSize(f->f_code->co_lnotab, + &lnotab, &lnotab_len); + addr = 0; + line = f->f_code->co_firstlineno; + new_lasti = -1; + for (offset = 0; offset < lnotab_len; offset += 2) { + addr += lnotab[offset]; + line += lnotab[offset+1]; + if (line >= new_lineno) { + new_lasti = addr; + new_lineno = line; + break; + } } } Index: Lib/test/test_trace.py =================================================================== --- Lib/test/test_trace.py (revision 72772) +++ Lib/test/test_trace.py (working copy) @@ -471,7 +471,7 @@ def trace(self, frame, event, arg): if not self.done and frame.f_code == self.function.func_code: firstLine = frame.f_code.co_firstlineno - if frame.f_lineno == firstLine + self.jumpFrom: + if event == 'line' and frame.f_lineno == firstLine + self.jumpFrom: # Cope with non-integer self.jumpTo (because of # no_jump_to_non_integers below). try: @@ -740,6 +740,27 @@ def test_19_no_jump_without_trace_function(self): no_jump_without_trace_function() + def test_jump_to_firstlineno(self): + # This tests that PDB can jump back to the first line in a + # file. See issue #1689458. It can only be triggered in a + # function call if the function is defined on a single line. + code = compile(""" +# Comments don't count. +output.append(2) # firstlineno is here. +output.append(3) +output.append(4) +""", "", "exec") + class fake_function: + func_code = code + jump = (2, 0) + tracer = JumpTracer(fake_function) + sys.settrace(tracer.trace) + namespace = {"output": []} + exec code in namespace + sys.settrace(None) + self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"]) + + def test_main(): test_support.run_unittest( TraceTestCase,