summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_trace.py23
-rw-r--r--Objects/frameobject.c37
2 files changed, 44 insertions, 16 deletions
diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py
index 48a717b..ecb588c 100644
--- a/Lib/test/test_trace.py
+++ b/Lib/test/test_trace.py
@@ -472,7 +472,7 @@ class JumpTracer:
def trace(self, frame, event, arg):
if not self.done and frame.f_code == self.function.__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:
@@ -741,6 +741,27 @@ class JumpTestCase(unittest.TestCase):
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)
+""", "<fake module>", "exec")
+ class fake_function:
+ __code__ = code
+ jump = (2, 0)
+ tracer = JumpTracer(fake_function)
+ sys.settrace(tracer.trace)
+ namespace = {"output": []}
+ exec(code, namespace)
+ sys.settrace(None)
+ self.compare_jump_output([2, 3, 2, 3, 4], namespace["output"])
+
+
def test_main():
support.run_unittest(
TraceTestCase,
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index e29c647..8bb0f9c 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -98,7 +98,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
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;
}
@@ -122,20 +123,26 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
new_lineno);
return -1;
}
-
- /* Find the bytecode offset for the start of the given line, or the
- * first code-owning line after it. */
- PyBytes_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. */
+ PyBytes_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;
+ }
}
}