diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -552,6 +552,37 @@ """ +def test_issue16653(): + """Test that a destructor is traced when dereferenced by a local variable. + + >>> class C: + ... def __del__(self): + ... x = 1 + + >>> def test_function(): + ... c = C() + ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... c = 1 + + >>> with PdbTestInput([ + ... 'step', + ... 'step', + ... 'continue', + ... ]): + ... test_function() + > (4)test_function() + -> c = 1 + (Pdb) step + --Call-- + > (2)__del__() + -> def __del__(self): + (Pdb) step + > (3)__del__() + -> x = 1 + (Pdb) continue + """ + + def pdb_invoke(method, arg): """Run pdb.method(arg).""" import pdb diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3841,12 +3841,7 @@ int result; if (tstate->tracing) return 0; - tstate->tracing++; - tstate->use_tracing = 0; result = func(obj, frame, what, arg); - tstate->use_tracing = ((tstate->c_tracefunc != NULL) - || (tstate->c_profilefunc != NULL)); - tstate->tracing--; return result; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -329,12 +329,13 @@ static PyObject * -call_trampoline(PyThreadState *tstate, PyObject* callback, +call_trampoline(PyThreadState *tstate, PyObject* callback, int istrace, PyFrameObject *frame, int what, PyObject *arg) { PyObject *args = PyTuple_New(3); PyObject *whatstr; PyObject *result; + Py_tracefunc tracefunc; if (args == NULL) return NULL; @@ -348,12 +349,29 @@ PyTuple_SET_ITEM(args, 1, whatstr); PyTuple_SET_ITEM(args, 2, arg); - /* call the Python-level function */ PyFrame_FastToLocals(frame); - result = PyEval_CallObject(callback, args); - PyFrame_LocalsToFast(frame, 1); - if (result == NULL) - PyTraceBack_Here(frame); + + /* Do no call the Python-level function when a destructor has been + traced/profiled in the call to PyFrame_FastToLocals and the + trace/profile function has been set to None from within the + corresponding Python-level function. */ + tracefunc = istrace ? tstate->c_tracefunc : tstate->c_profilefunc; + if (tracefunc == NULL) { + result = Py_None; + Py_INCREF(Py_None); + } + else { + /* call the Python-level function */ + tstate->tracing++; + tstate->use_tracing = 0; + result = PyEval_CallObject(callback, args); + PyFrame_LocalsToFast(frame, 1); + if (result == NULL) + PyTraceBack_Here(frame); + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + tstate->tracing--; + } /* cleanup */ Py_DECREF(args); @@ -369,7 +387,7 @@ if (arg == NULL) arg = Py_None; - result = call_trampoline(tstate, self, frame, what, arg); + result = call_trampoline(tstate, self, 0, frame, what, arg); if (result == NULL) { PyEval_SetProfile(NULL, NULL); return -1; @@ -392,7 +410,7 @@ callback = frame->f_trace; if (callback == NULL) return 0; - result = call_trampoline(tstate, callback, frame, what, arg); + result = call_trampoline(tstate, callback, 1, frame, what, arg); if (result == NULL) { PyEval_SetTrace(NULL, NULL); Py_XDECREF(frame->f_trace);