Index: Python/ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.332 diff -c -r2.332 ceval.c *** Python/ceval.c 23 Aug 2002 14:11:35 -0000 2.332 --- Python/ceval.c 27 Aug 2002 13:12:02 -0000 *************** *** 2934,2946 **** opcode. */ ! if (opcode != POP_TOP && opcode != RETURN_NONE && ! (frame->f_lasti < *instr_lb || frame->f_lasti > *instr_ub)) { PyCodeObject* co = frame->f_code; int size, addr; unsigned char* p; ! call_trace(func, obj, frame, PyTrace_LINE, Py_None); size = PyString_Size(co->co_lnotab) / 2; p = (unsigned char*)PyString_AsString(co->co_lnotab); --- 2934,2946 ---- opcode. */ ! if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) { PyCodeObject* co = frame->f_code; int size, addr; unsigned char* p; + int trace_called = 0; ! /* call_trace(func, obj, frame, PyTrace_LINE, Py_None);*/ size = PyString_Size(co->co_lnotab) / 2; p = (unsigned char*)PyString_AsString(co->co_lnotab); *************** *** 2959,2964 **** --- 2959,2969 ---- increments gets confusing, to say the least. */ while (size >= 0) { + if (!trace_called && addr == frame->f_lasti) { + call_trace(func, obj, frame, + PyTrace_LINE, Py_None); + trace_called = 1; + } if (addr + *p > frame->f_lasti) break; addr += *p++; *************** *** 2967,2973 **** } *instr_lb = addr; if (size > 0) { ! while (--size >= 0) { addr += *p++; if (*p++) break; --- 2972,2978 ---- } *instr_lb = addr; if (size > 0) { ! while (--size > 0) { addr += *p++; if (*p++) break; Index: Lib/test/test_trace.py =================================================================== RCS file: Lib/test/test_trace.py diff -N Lib/test/test_trace.py *** /dev/null 1 Jan 1970 00:00:00 -0000 --- Lib/test/test_trace.py 27 Aug 2002 13:12:02 -0000 *************** *** 0 **** --- 1,110 ---- + # Testing the line trace facility. + + from test import test_support + import unittest + import sys + import pprint + import os + import difflib + + # A very basic example. If this fails, we're in deep trouble. + def basic(): + return 1 + + basic.events = [(0, 'call'), + (1, 'line'), + (1, 'return')] + + # Armin Rigo's failing example: + def arigo_example(): + x = 1 + del x + while 0: + pass + x = 1 + + arigo_example.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (3, 'line'), + (5, 'line'), + (5, 'return')] + + # check that lines consisting of just one instruction get traced: + def one_instr_line(): + x = 1 + del x + x = 1 + + one_instr_line.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (3, 'line'), + (3, 'return')] + + def no_pop_tops(): # 0 + for a in range(2): # 1 + if a: # 2 + x = 1 # 3 + else: # 4 + x = 1 # 5 + + no_pop_tops.events = [(0, 'call'), + (1, 'line'), + (2, 'line'), + (5, 'line'), + (1, 'line'), + (2, 'line'), + (3, 'line'), + (1, 'line'), + (5, 'return')] + + def no_pop_blocks(): + while 0: + bla + x = 1 + + no_pop_blocks.events = [(0, 'call'), + (1, 'line'), + (3, 'line'), + (3, 'return')] + + class Tracer: + def __init__(self): + self.events = [] + def trace(self, frame, event, arg): + self.events.append((frame.f_lineno, event)) + return self.trace + + class TraceTestCase(unittest.TestCase): + def run_test(self, func): + tracer = Tracer() + sys.settrace(tracer.trace) + func() + sys.settrace(None) + fl = func.func_code.co_firstlineno + expected_events = [(l + fl, e) for (l, e) in func.events] + if tracer.events != expected_events: + self.fail( + "events did not match expectation:\n" + + "\n".join(difflib.ndiff(map(str, expected_events), + map(str, tracer.events)))) + + def test_basic(self): + self.run_test(basic) + def test_arigo(self): + self.run_test(arigo_example) + def test_one_instr(self): + self.run_test(one_instr_line) + def test_no_pop_blocks(self): + self.run_test(no_pop_blocks) + def test_no_pop_tops(self): + self.run_test(no_pop_tops) + + + + def test_main(): + test_support.run_unittest(TraceTestCase) + + if __name__ == "__main__": + test_main()