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 5efbafe084..96d164fb7e 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 @@ -1,4 +1,5 @@ -from _pydevd_bundle.pydevd_constants import GlobalDebuggerHolder, ForkSafeLock +from _pydevd_bundle.pydevd_constants import GlobalDebuggerHolder, ForkSafeLock, \ + PYDEVD_IPYTHON_CONTEXT import threading from _pydevd_bundle.pydevd_additional_thread_info import _set_additional_thread_info_lock, PyDBAdditionalThreadInfo from pydevd_file_utils import NORM_PATHS_AND_BASE_CONTAINER, \ @@ -315,25 +316,25 @@ def _line_event(code, line): if func_code_info.always_skip_code: return monitor.DISABLE - additional_info = thread_info.additional_info + info = thread_info.additional_info # We know the frame depth. frame = sys._getframe(1) - pydev_step_cmd = additional_info.pydev_step_cmd - is_stepping = pydev_step_cmd != -1 + step_cmd = info.pydev_step_cmd + is_stepping = step_cmd != -1 if py_db.is_files_filter_enabled: if func_code_info.filtered_out is None: if py_db.apply_files_filter(frame, func_code_info.abs_path_filename, False): - if is_stepping and additional_info.pydev_original_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE) and not _global_notify_skipped_step_in: + if is_stepping and info.pydev_original_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE) and not _global_notify_skipped_step_in: notify_skipped_step_in_because_of_filters(py_db, frame) # A little gotcha, sometimes when we're stepping in we have to stop in a # return event showing the back frame as the current frame, so, we need # to check not only the current frame but the back frame too. back_frame = frame.f_back - if back_frame is not None and pydev_step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): + if back_frame is not None and step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): if py_db.apply_files_filter(back_frame, back_frame.f_code.co_filename, False): func_code_info.filtered_out = True return monitor.DISABLE @@ -382,12 +383,12 @@ def _line_event(code, line): # 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) + py_db.handle_breakpoint_expression(bp, 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) + eval_result = py_db.handle_breakpoint_condition(info, bp, new_frame) if not eval_result: stop = False stop_on_plugin_breakpoint = False @@ -397,14 +398,13 @@ def _line_event(code, line): 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') + if info.pydev_message is not None and len(info.pydev_message) > 0: + cmd = py_db.cmd_factory.make_io_message(info.pydev_message + os.linesep, '1') py_db.writer.add_command(cmd) if stop: - thread = thread_info.thread py_db.set_suspend( - thread, + thread_info.thread, stop_reason, suspend_other_threads=bp and bp.suspend_policy == "ALL", ) @@ -413,9 +413,52 @@ def _line_event(code, line): # if result: # frame = result - if additional_info.pydev_state == STATE_SUSPEND: + if info.pydev_state == STATE_SUSPEND: print('suspend...') - py_db.do_wait_suspend(thread, frame, 'line', None) + py_db.do_wait_suspend(thread_info.thread, frame, 'line', None) + return + + # Ok, did not suspend due to a breakpoint, let's see if we're stepping. + stop_frame = info.pydev_step_stop + if step_cmd == -1: + return + + elif step_cmd in (CMD_STEP_OVER, CMD_STEP_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_RETURN_MY_CODE) and _is_same_frame(info, stop_frame, frame): + py_db.set_suspend(thread_info.thread, step_cmd, original_step_cmd=info.pydev_original_step_cmd) + py_db.do_wait_suspend(thread_info.thread, frame, 'line', None) + return + + elif step_cmd == CMD_SMART_STEP_INTO and ( + stop_frame is not None and + stop_frame is not frame and + stop_frame is not frame.f_back and + (frame.f_back is None or stop_frame is not frame.f_back.f_back)): + can_skip = True + + elif step_cmd == CMD_STEP_INTO_MY_CODE: + if ( + py_db.apply_files_filter(frame, frame.f_code.co_filename, True) + and (frame.f_back is None or py_db.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, True)) + ): + can_skip = True + + elif step_cmd == CMD_STEP_INTO_COROUTINE: + f = frame + while f is not None: + if _is_same_frame(info, stop_frame, f): + break + f = f.f_back + else: + can_skip = True + + # if can_skip: + # if plugin_manager is not None and ( + # py_db.has_plugin_line_breaks or py_db.has_plugin_exception_breaks): + # can_skip = plugin_manager.can_skip(py_db, frame) + # + # if can_skip and py_db.show_return_values and info.pydev_step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE) and _is_same_frame(info, stop_frame, frame.f_back): + # # trace function for showing return values after step over + # can_skip = False def _start_method(code, instruction_offset): @@ -527,3 +570,23 @@ def restart_events(): print('restart events') sys.monitoring.restart_events() + +def _is_same_frame(info, target_frame, current_frame): + if target_frame is current_frame: + return True + + if info.pydev_use_scoped_step_frame: + # If using scoped step we don't check the target, we just need to check + # if the current matches the same heuristic where the target was defined. + if target_frame is not None and current_frame is not None: + if target_frame.f_code.co_filename == current_frame.f_code.co_filename: + # The co_name may be different (it may include the line number), but + # the filename must still be the same. + f = current_frame.f_back + if f is not None and f.f_code.co_name == PYDEVD_IPYTHON_CONTEXT[1]: + f = f.f_back + if f is not None and f.f_code.co_name == PYDEVD_IPYTHON_CONTEXT[2]: + return True + + return False +