mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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
This commit is contained in:
committed by
intellij-monorepo-bot
parent
23e425ee60
commit
ba90318d2d
@@ -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('<cell line:')
|
||||
and other_frame.f_code.co_name.startswith('<cell line:'))
|
||||
@@ -177,17 +191,18 @@ def get_breakpoint(plugin, pydb, frame, event, args):
|
||||
filename = frame.f_code.co_filename
|
||||
frame_line = frame.f_lineno
|
||||
if event == "line":
|
||||
cell_info = pydb.cell_info
|
||||
if is_cell_filename(filename):
|
||||
if filename not in cell_info.cell_filename_to_cell_id_map:
|
||||
cell_info.cache_cell_mapping(filename)
|
||||
if hasattr(pydb, 'cell_info'):
|
||||
cell_info = pydb.cell_info
|
||||
if is_cell_filename(filename):
|
||||
if filename not in cell_info.cell_filename_to_cell_id_map:
|
||||
cell_info.cache_cell_mapping(filename)
|
||||
|
||||
cell_id = cell_info.cell_filename_to_cell_id_map[filename]
|
||||
if cell_id in pydb.jupyter_breakpoints:
|
||||
line_to_bp = pydb.jupyter_breakpoints[cell_id]
|
||||
if frame_line in line_to_bp:
|
||||
bp = line_to_bp[frame_line]
|
||||
return True, bp, frame, "jupyter-line"
|
||||
cell_id = cell_info.cell_filename_to_cell_id_map[filename]
|
||||
if cell_id in pydb.jupyter_breakpoints:
|
||||
line_to_bp = pydb.jupyter_breakpoints[cell_id]
|
||||
if frame_line in line_to_bp:
|
||||
bp = line_to_bp[frame_line]
|
||||
return True, bp, frame, "jupyter-line"
|
||||
return False
|
||||
|
||||
|
||||
@@ -215,13 +230,14 @@ def _is_inside_jupyter_cell(frame, pydb):
|
||||
while frame is not None:
|
||||
filename = frame.f_code.co_filename
|
||||
file_basename = os.path.basename(filename)
|
||||
if is_cell_filename(filename):
|
||||
if filename not in pydb.cell_info.cell_filename_to_cell_id_map:
|
||||
send_cell_modified_warning_once(pydb, file_basename)
|
||||
# IDE side doesn't know about the cell, so we should ignore it
|
||||
return False
|
||||
return True
|
||||
frame = frame.f_back
|
||||
if hasattr(pydb, 'cell_info'):
|
||||
if is_cell_filename(filename):
|
||||
if filename not in pydb.cell_info.cell_filename_to_cell_id_map:
|
||||
send_cell_modified_warning_once(pydb, file_basename)
|
||||
# IDE side doesn't know about the cell, so we should ignore it
|
||||
return False
|
||||
return True
|
||||
frame = frame.f_back
|
||||
return False
|
||||
|
||||
|
||||
@@ -255,7 +271,7 @@ def exception_break(plugin, pydb, frame, args, arg):
|
||||
|
||||
def _convert_filename(frame, pydb):
|
||||
filename = frame.f_code.co_filename
|
||||
if is_cell_filename(filename):
|
||||
if hasattr(pydb, 'cell_info') and is_cell_filename(filename):
|
||||
return pydb.cell_info.cell_filename_to_cell_id_map[filename]
|
||||
else:
|
||||
return filename
|
||||
|
||||
@@ -79,6 +79,7 @@ def attach_to_debugger(debugger_port):
|
||||
debugger = pydevd.PyDB()
|
||||
debugger.frame_eval_func = None
|
||||
ipython_shell.debugger = debugger
|
||||
reset_threads_debug_state()
|
||||
try:
|
||||
debugger.connect(pydev_localhost.get_localhost(), debugger_port)
|
||||
debugger.prepare_to_run(enable_tracing_from_start=False)
|
||||
@@ -136,3 +137,32 @@ def is_cell_filename(filename):
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def reset_threads_debug_state():
|
||||
"""
|
||||
Resets the debugging state of all non-daemon threads to their initial values.
|
||||
|
||||
This is called when initializing or reinitializing the debugger
|
||||
to ensure all threads start with a clean debugging state.
|
||||
"""
|
||||
try:
|
||||
import threading
|
||||
from _pydevd_bundle.pydevd_trace_dispatch_regular import set_additional_thread_info
|
||||
# get_non_pydevd_threads
|
||||
all_threads = threading.enumerate()
|
||||
non_pydevd_threads = [t for t in all_threads if t and not getattr(t, 'is_pydev_daemon_thread', False)]
|
||||
|
||||
for t in non_pydevd_threads:
|
||||
if t is None:
|
||||
continue
|
||||
try:
|
||||
additional_info = set_additional_thread_info(t)
|
||||
additional_info.pydev_step_cmd = -1
|
||||
additional_info.pydev_step_stop = None
|
||||
additional_info.pydev_state = 1 # STATE_RUN
|
||||
except:
|
||||
sys.stderr.write('Jupyter Debugger Plugin: Unable to reset debug state for thread. Debug functionality may be incorrect\n')
|
||||
|
||||
except:
|
||||
sys.stderr.write('Jupyter Debugger Plugin: Failed to initialize thread debug states. Debugger may not function correctly\n')
|
||||
Reference in New Issue
Block a user