Index: Python/compile.c =================================================================== --- Python/compile.c (revision 60427) +++ Python/compile.c (working copy) @@ -43,6 +43,7 @@ unsigned i_jabs : 1; unsigned i_jrel : 1; unsigned i_hasarg : 1; + unsigned i_istarget : 1; /* set if we are the target of a jump */ unsigned char i_opcode; int i_oparg; struct basicblock_ *i_target; /* target block (if jump instruction) */ @@ -1065,11 +1066,15 @@ #define ADDOP_JABS(C, OP, O) { \ if (!compiler_addop_j((C), (OP), (O), 1)) \ return 0; \ + if (O->b_instr) \ + O->b_instr->i_istarget = 1; \ } #define ADDOP_JREL(C, OP, O) { \ if (!compiler_addop_j((C), (OP), (O), 0)) \ return 0; \ + if (O->b_instr) \ + O->b_instr->i_istarget = 1; \ } /* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use @@ -1625,6 +1630,10 @@ if (!compiler_push_fblock(c, LOOP, loop)) return 0; if (constant == -1) { + /* XXX(nnorwitz): is there a better way to handle this? + for loops are special, we want to be able to trace them + each time around, so we need to set an extra line number. */ + c->u->u_lineno_set = false; VISIT(c, expr, s->v.While.test); ADDOP_JREL(c, JUMP_IF_FALSE, anchor); ADDOP(c, POP_TOP); @@ -3518,10 +3527,9 @@ assert(d_bytecode >= 0); assert(d_lineno >= 0); - /* XXX(nnorwitz): is there a better way to handle this? - for loops are special, we want to be able to trace them - each time around, so we need to set an extra line number. */ - if (d_lineno == 0 && i->i_opcode != FOR_ITER) + /* Some instructions are jump targets, we want to be able + to trace them even if they do not start a new line. */ + if (d_lineno == 0 && !i->i_istarget) return 1; if (d_bytecode > 255) { Index: Lib/test/test_trace.py =================================================================== --- Lib/test/test_trace.py (revision 60427) +++ Lib/test/test_trace.py (working copy) @@ -243,6 +243,38 @@ self.events.append((frame.f_lineno, event)) return self.trace + +def for_example(): + for x in range(3): + pass + +for_example.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (1, 'line'), + (2, 'line'), + (1, 'line'), + (2, 'line'), + (1, 'line'), + (1, 'return')] + +def while_example(): + # While expression should be traced on every loop + x = 2 + while x > 0: + x -= 1 + +while_example.events = [(0, 'call'), + (2, 'line'), + (3, 'line'), + (4, 'line'), + (3, 'line'), + (4, 'line'), + (3, 'line'), + (3, 'return')] + + + class TraceTestCase(unittest.TestCase): def compare_events(self, line_offset, events, expected_events): events = [(l - line_offset, e) for (l, e) in events] @@ -321,6 +353,11 @@ self.compare_events(generator_example.__code__.co_firstlineno, tracer.events, generator_example.events) + def test_14_loops(self): + # issue1750076: "while" expression is skipped by debugger + self.run_test(for_example) + self.run_test(while_example) + class RaisingTraceFuncTestCase(unittest.TestCase): def trace(self, frame, event, arg): """A trace function that raises an exception in response to a