The following debugging session, run with python on the default branch, shows that pdb does not stop in __del__ when it is invoked. The reason is: - The destructor is not called when processing the 'c = 1' statement because foo frame.f_locals owns a reference to the C instance. - When the interpreter is about to invoke the trace function with the ensuing debug event, the call to PyFrame_LocalsToFast in call_trampoline causes the destructor to be invoked (as shown by gdb), and the destructor is not traced because at that time tstate->use_tracing is false. This is confirmed by the fact that when 'c = 1' is replaced with 'c = 1; locals()' (on one single line so as not to trigger the trace function before the call to locals()), then pdb stops in __del__. === foo.py ================ class C: def __del__(self): print("Calling C destructor.") def foo(): c = C() import pdb; pdb.set_trace() c = 1 foo() =============================== $ ./python /tmp/foo.py > /tmp/foo.py(8)foo() -> c = 1 (Pdb) step Calling C destructor. --Return-- > /tmp/foo.py(8)foo()->None -> c = 1 (Pdb) ===============================
Tracing/profiling is disabled when tstate->tracing is true or tstate->use_tracing is false. The proposed patch fixes the problem by reducing the scope where this condition is true. As a consequence call_trace, profile_trampoline, trace_trampoline and call_trampoline may now be called recursively. The patch includes a test case.