diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 91112e8b211..d5fb0e504d8 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -178,23 +178,48 @@ class TraceTestCase(unittest.TestCase): self.run_test2(settrace_and_raise) class RaisingTraceFuncTestCase(unittest.TestCase): - def test_it(self): - def tr(frame, event, arg): + def trace(self, frame, event, arg): + """A trace function that raises an exception in response to a + specific trace event.""" + if event == self.raiseOnEvent: raise ValueError # just something that isn't RuntimeError - def f(): + else: + return self.trace + + def f(self): + """The function to trace; raises an exception if that's the case + we're testing, so that the 'exception' trace event fires.""" + if self.raiseOnEvent == 'exception': + x = 0 + y = 1/x + else: return 1 + + def run_test_for_event(self, event): + """Tests that an exception raised in response to the given event is + handled OK.""" + self.raiseOnEvent = event try: for i in xrange(sys.getrecursionlimit() + 1): - sys.settrace(tr) + sys.settrace(self.trace) try: - f() + self.f() except ValueError: pass else: self.fail("exception not thrown!") except RuntimeError: self.fail("recursion counter not reset") - + + # Test the handling of exceptions raised by each kind of trace event. + def test_call(self): + self.run_test_for_event('call') + def test_line(self): + self.run_test_for_event('line') + def test_return(self): + self.run_test_for_event('return') + def test_exception(self): + self.run_test_for_event('exception') def test_main(): test_support.run_unittest(TraceTestCase) diff --git a/Python/ceval.c b/Python/ceval.c index 1c95cb9cfba..e654efd462c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -51,7 +51,7 @@ static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *, static void call_trace_protected(Py_tracefunc, PyObject *, PyFrameObject *, int); static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *); -static void maybe_call_line_trace(Py_tracefunc, PyObject *, +static int maybe_call_line_trace(Py_tracefunc, PyObject *, PyFrameObject *, int *, int *); static PyObject *apply_slice(PyObject *, PyObject *, PyObject *); @@ -726,9 +726,14 @@ eval_frame(PyFrameObject *f) /* see maybe_call_line_trace for expository comments */ f->f_stacktop = stack_pointer; - maybe_call_line_trace(tstate->c_tracefunc, - tstate->c_traceobj, - f, &instr_lb, &instr_ub); + + if (maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + f, &instr_lb, &instr_ub)) { + /* trace function raised an exception */ + why = WHY_EXCEPTION; + goto on_error; + } /* Reload possibly changed frame fields */ JUMPTO(f->f_lasti); stack_pointer = f->f_stacktop; @@ -2872,7 +2877,7 @@ call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, return result; } -static void +static int maybe_call_line_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, int *instr_lb, int *instr_ub) { @@ -2947,6 +2952,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, suggested by f_lasti on this one occasion where it's desirable. */ + int result = 0; + if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) { PyCodeObject* co = frame->f_code; int size, addr, line; @@ -2980,8 +2987,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, if (addr == frame->f_lasti) { frame->f_lineno = line; - call_trace(func, obj, frame, - PyTrace_LINE, Py_None); + result = call_trace(func, obj, frame, + PyTrace_LINE, Py_None); } if (size > 0) { @@ -2996,6 +3003,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, *instr_ub = INT_MAX; } } + + return result; } void