From ba90318d2da90cc28e19f2dcfd05579ab6353e87 Mon Sep 17 00:00:00 2001 From: "Natalia.Murycheva" Date: Wed, 23 Jul 2025 17:58:16 +0200 Subject: [PATCH] [J-Debugger] PY-82513 Subsequent debug sessions don't start correctly #(PY-82513, PY-64509, PY-79518) Ready for Merge This is a flaky bug: in the case of the second and following debug sessions, the Jupyter Debugger stops at the random place at the debug beginning. In Python Debugger, we add to threads an additional attribute called additional_thread_info. The debugger's additional_thread_info stores the current execution state (run/suspended) and step commands. **When these values weren't cleared after a debug session ended,** threads with "suspended" state would carry over to the next session, causing it to stop immediately upon start. Fixed by properly resetting threads' debug state between debug sessions. GitOrigin-RevId: ba23933f616c79f9a9e0307f0830f53eebc84ccd --- .../jupyter_debug/pydev_jupyter_plugin.py | 68 ++++++++++++------- .../jupyter_debug/pydev_jupyter_utils.py | 30 ++++++++ 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/python/helpers/jupyter_debug/pydev_jupyter_plugin.py b/python/helpers/jupyter_debug/pydev_jupyter_plugin.py index 9c1278878fb0..488c7129f097 100644 --- a/python/helpers/jupyter_debug/pydev_jupyter_plugin.py +++ b/python/helpers/jupyter_debug/pydev_jupyter_plugin.py @@ -26,8 +26,15 @@ class JupyterLineBreakpoint(LineBreakpoint): def add_line_breakpoint(plugin, pydb, type, file, line, condition, expression, func_name, hit_condition=None, is_logpoint=False): if type == 'jupyter-line': - breakpoint = JupyterLineBreakpoint(file, line, condition, func_name, expression, hit_condition=hit_condition, - is_logpoint=is_logpoint) + breakpoint = JupyterLineBreakpoint( + file, + line, + condition, + func_name, + expression, + hit_condition=hit_condition, + is_logpoint=is_logpoint + ) if not hasattr(pydb, 'jupyter_breakpoints'): _init_plugin_breaks(pydb) return breakpoint, pydb.jupyter_breakpoints @@ -61,7 +68,8 @@ def remove_exception_breakpoint(plugin, pydb, type, exception): def get_breakpoints(plugin, pydb, type): if type == 'jupyter-line': - return pydb.jupyter_breakpoints + if hasattr(pydb, 'jupyter_breakpoints'): + return pydb.jupyter_breakpoints return None @@ -70,8 +78,9 @@ def change_variable(plugin, frame, attr, expression): def has_exception_breaks(plugin): - if len(plugin.main_debugger.jupyter_exception_break) > 0: - return True + if hasattr(plugin.main_debugger, 'jupyter_exception_break'): + if len(plugin.main_debugger.jupyter_exception_break) > 0: + return True return False @@ -81,9 +90,10 @@ def has_exception_breaks(plugin): def has_line_breaks(plugin): - for file, breakpoints in dict_iter_items(plugin.main_debugger.jupyter_breakpoints): - if len(breakpoints) > 0: - return True + if hasattr(plugin.main_debugger, 'jupyter_breakpoints'): + for file, breakpoints in dict_iter_items(plugin.main_debugger.jupyter_breakpoints): + if len(breakpoints) > 0: + return True return False @@ -91,6 +101,8 @@ def can_not_skip(plugin, pydb, frame, info): step_cmd = info.pydev_step_cmd if step_cmd == 108 and _is_equals(frame, _get_stop_frame(info)): return True + if not hasattr(pydb, 'cell_info') or not hasattr(pydb, 'jupyter_breakpoints'): + return False if pydb.jupyter_breakpoints: filename = frame.f_code.co_filename cell_info = pydb.cell_info @@ -125,6 +137,8 @@ def _is_equals(frame, other_frame): # We can't compare frames directly, because Jupyter compiles ast nodes # in cell separately. At the same time, the frame filename is unique and stays # the same within a cell. + if frame is None or other_frame is None: + return False return frame.f_code.co_filename == other_frame.f_code.co_filename \ and ((frame.f_code.co_name.startswith('