diff --git a/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_frame.py b/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_frame.py index 387516b66f..86ceb49396 100644 --- a/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_frame.py +++ b/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_frame.py @@ -912,7 +912,7 @@ def trace_dispatch(self, frame, event, arg): stop = True elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: - result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) + result = plugin_manager.get_breakpoint(main_debugger, frame, event, self._args) if result: stop_on_plugin_breakpoint, breakpoint, new_frame, bp_type = result diff --git a/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_trace_api.py b/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_trace_api.py index 77e8b3fadf..d37e969aa6 100644 --- a/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_trace_api.py +++ b/plugins/org.python.pydev.core/pysrc/_pydevd_bundle/pydevd_trace_api.py @@ -46,7 +46,7 @@ def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd): return False -def get_breakpoint(plugin, pydb, pydb_frame, frame, event, args): +def get_breakpoint(plugin, pydb, frame, event, args): return None diff --git a/plugins/org.python.pydev.core/pysrc/_pydevd_sys_monitoring/pydevd_sys_monitoring.py b/plugins/org.python.pydev.core/pysrc/_pydevd_sys_monitoring/pydevd_sys_monitoring.py index a2dc078fdd..5efbafe084 100644 --- a/plugins/org.python.pydev.core/pysrc/_pydevd_sys_monitoring/pydevd_sys_monitoring.py +++ b/plugins/org.python.pydev.core/pysrc/_pydevd_sys_monitoring/pydevd_sys_monitoring.py @@ -9,6 +9,7 @@ from types import CodeType from typing import Dict, Optional, Set import traceback +import os DEBUGGER_ID = sys.monitoring.DEBUGGER_ID monitor = sys.monitoring @@ -29,6 +30,7 @@ CMD_STEP_OVER_MY_CODE: int = 159 CMD_STEP_RETURN_MY_CODE: int = 160 CMD_SET_BREAK: int = 111 +CMD_SET_FUNCTION_BREAK: int = 208 # Cache where we should keep that we completely skipped entering some context. # It needs to be invalidated when: @@ -145,7 +147,7 @@ def __init__(self): self.breakpoints_mtime: int = -1 self.bp_line_to_breakpoint: Set[int] = set() - self.function_breakpoint: bool = False + self.function_breakpoint = None self.filtered_out: Optional[bool] = None @@ -253,7 +255,7 @@ def get_func_code_info(thread_info: ThreadInfo, code_obj, code_to_func_code_info if function_breakpoint: # Go directly into tracing mode func_code_info.breakpoint_found = True - func_code_info.function_breakpoint = True + func_code_info.function_breakpoint = function_breakpoint if breakpoints: # if DEBUG: @@ -267,7 +269,9 @@ def get_func_code_info(thread_info: ThreadInfo, code_obj, code_to_func_code_info if breakpoint_line in line_to_offset: bp_line_to_breakpoint[breakpoint_line] = bp - func_code_info.breakpoint_found = bool(bp_line_to_breakpoint) + func_code_info.first_code_line = code_line_info.first_line + + func_code_info.breakpoint_found = bool(bp_line_to_breakpoint or function_breakpoint) func_code_info.bp_line_to_breakpoint = bp_line_to_breakpoint code_to_func_code_info[code_obj] = func_code_info_obj @@ -343,16 +347,73 @@ def _line_event(code, line): func_code_info.filtered_out = False # If we reached here, it was not filtered out. + bp = None + stop = False if func_code_info.breakpoint_found: - bp = func_code_info.bp_line_to_breakpoint.get(line) - if bp is not None: - stop_reason = CMD_SET_BREAK + stop_info = {} + stop_reason = CMD_SET_BREAK + bp_type = None + + if func_code_info.function_breakpoint and line == func_code_info.first_code_line: + bp = func_code_info.function_breakpoint + stop = True + new_frame = frame + stop_reason = CMD_SET_FUNCTION_BREAK + + else: + bp = func_code_info.bp_line_to_breakpoint.get(line) + if bp is not None: + new_frame = frame + stop = True + + if bp is None: + plugin_manager = py_db.plugin + stop_on_plugin_breakpoint = False + # return is not taken into account for breakpoint hit because we'd have a double-hit in this case + # (one for the line and the other for the return). + + # TODO: Support plugins for breakpoints (needs some refactoring) + # if plugin_manager is not None and py_db.has_plugin_line_breaks: + # result = plugin_manager.get_breakpoint(py_db, frame, event, self._args) + # if result: + # stop_on_plugin_breakpoint, bp, new_frame, bp_type = result + + if bp: + # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint + # lets do the conditional stuff here + if bp.expression is not None: + py_db.handle_breakpoint_expression(bp, additional_info, new_frame) + + if stop or stop_on_plugin_breakpoint: + eval_result = False + if bp.has_condition: + eval_result = py_db.handle_breakpoint_condition(additional_info, bp, new_frame) + if not eval_result: + stop = False + stop_on_plugin_breakpoint = False + + # Handle logpoint (on a logpoint we should never stop). + if (stop or stop_on_plugin_breakpoint) and bp.is_logpoint: + stop = False + stop_on_plugin_breakpoint = False + + if additional_info.pydev_message is not None and len(additional_info.pydev_message) > 0: + cmd = py_db.cmd_factory.make_io_message(additional_info.pydev_message + os.linesep, '1') + py_db.writer.add_command(cmd) + + if stop: thread = thread_info.thread py_db.set_suspend( thread, stop_reason, suspend_other_threads=bp and bp.suspend_policy == "ALL", ) + # elif stop_on_plugin_breakpoint and plugin_manager is not None: + # result = plugin_manager.suspend(py_db, thread, frame, bp_type) + # if result: + # frame = result + + if additional_info.pydev_state == STATE_SUSPEND: print('suspend...') py_db.do_wait_suspend(thread, frame, 'line', None) diff --git a/plugins/org.python.pydev.core/pysrc/pydevd_plugins/django_debug.py b/plugins/org.python.pydev.core/pysrc/pydevd_plugins/django_debug.py index ff7f1eb93b..100f65af35 100644 --- a/plugins/org.python.pydev.core/pysrc/pydevd_plugins/django_debug.py +++ b/plugins/org.python.pydev.core/pysrc/pydevd_plugins/django_debug.py @@ -504,7 +504,7 @@ def stop(plugin, main_debugger, frame, event, args, stop_info, arg, step_cmd): return False -def get_breakpoint(plugin, py_db, pydb_frame, frame, event, args): +def get_breakpoint(plugin, py_db, frame, event, args): py_db = args[0] _filename = args[1] info = args[2] diff --git a/plugins/org.python.pydev.core/pysrc/pydevd_plugins/jinja2_debug.py b/plugins/org.python.pydev.core/pysrc/pydevd_plugins/jinja2_debug.py index a5e4a000cc..8ba9b9ddae 100644 --- a/plugins/org.python.pydev.core/pysrc/pydevd_plugins/jinja2_debug.py +++ b/plugins/org.python.pydev.core/pysrc/pydevd_plugins/jinja2_debug.py @@ -430,7 +430,7 @@ def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd): return False -def get_breakpoint(plugin, py_db, pydb_frame, frame, event, args): +def get_breakpoint(plugin, py_db, frame, event, args): py_db = args[0] _filename = args[1] info = args[2] diff --git a/plugins/org.python.pydev.core/pysrc/tests_python/test_sys_monitoring.py b/plugins/org.python.pydev.core/pysrc/tests_python/test_sys_monitoring.py index c1646ac41e..c215c84695 100644 --- a/plugins/org.python.pydev.core/pysrc/tests_python/test_sys_monitoring.py +++ b/plugins/org.python.pydev.core/pysrc/tests_python/test_sys_monitoring.py @@ -22,6 +22,25 @@ def with_monitoring(): _disable_monitoring() +def test_variables_on_call(with_monitoring): + monitor.set_events(DEBUGGER_ID, monitor.events.PY_START) + + found = [] + + def _start_method(code, offset): + if code.co_name == 'method': + frame = sys._getframe(1) + found.append(frame.f_locals['arg1']) + + monitor.register_callback(DEBUGGER_ID, monitor.events.PY_START , _start_method) + + def method(arg1): + pass + + method(22) + assert found == [22] + + def test_disabling_code(with_monitoring): executed = []