From 6b7c257bec72a4ee917b6ad998bdd6b59cb31836 Mon Sep 17 00:00:00 2001 From: Elizaveta Shashkova Date: Tue, 23 Oct 2018 14:37:15 +0300 Subject: [PATCH] PY-32319 Stop at file without breakpoints as well and stop at correct line --- .../_pydevd_bundle/pydevd_breakpointhook.py | 42 +++++++++++ .../pydevd_frame_tracing.py | 2 +- python/helpers/pydev/pydevd.py | 69 ++++++++----------- 3 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 python/helpers/pydev/_pydevd_bundle/pydevd_breakpointhook.py diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_breakpointhook.py b/python/helpers/pydev/_pydevd_bundle/pydevd_breakpointhook.py new file mode 100644 index 000000000000..09e6a40d0db0 --- /dev/null +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_breakpointhook.py @@ -0,0 +1,42 @@ + +# This file shouldn't be added to DONT_TRACE dict + +import sys +import os + + +def breakpointhook(*args, **kwargs): + # It's necessary to use inner function to add extra frame + def breakpoint(): + hookname = os.getenv('PYTHONBREAKPOINT') + if hookname is not None and len(hookname) > 0 and hasattr(sys, '__breakpointhook__'): + sys.__breakpointhook__(*args, **kwargs) + else: + import pydevd + py_db = pydevd.get_global_debugger() + if (py_db is not None) and (py_db.frame_eval_func is not None): + from _pydevd_frame_eval.pydevd_frame_tracing import suspend_at_builtin_breakpoint + suspend_at_builtin_breakpoint() + else: + pydevd.settrace( + suspend=True, + trace_only_current_thread=True, + patch_multiprocessing=False, + stop_at_frame=sys._getframe(), + ) + breakpoint() + + +def install_breakpointhook(pydevd_breakpointhook=None): + if pydevd_breakpointhook is None: + pydevd_breakpointhook = breakpointhook + if sys.version_info[0:2] >= (3, 7): + # There are some choices on how to provide the breakpoint hook. Namely, we can provide a + # PYTHONBREAKPOINT which provides the import path for a method to be executed or we + # can override sys.breakpointhook. + # pydevd overrides sys.breakpointhook instead of providing an environment variable because + # it's possible that the debugger starts the user program but is not available in the + # PYTHONPATH (and would thus fail to be imported if PYTHONBREAKPOINT was set to pydevd.settrace). + # Note that the implementation still takes PYTHONBREAKPOINT in account (so, if it was provided + # by someone else, it'd still work). + sys.breakpointhook = pydevd_breakpointhook diff --git a/python/helpers/pydev/_pydevd_frame_eval/pydevd_frame_tracing.py b/python/helpers/pydev/_pydevd_frame_eval/pydevd_frame_tracing.py index 45e2c595a87b..2d629944d727 100644 --- a/python/helpers/pydev/_pydevd_frame_eval/pydevd_frame_tracing.py +++ b/python/helpers/pydev/_pydevd_frame_eval/pydevd_frame_tracing.py @@ -52,7 +52,7 @@ def _get_line_for_frame(frame): def suspend_at_builtin_breakpoint(): # used by built-in breakpoint() function appeared in Python 3.7 - frame = sys._getframe(2) + frame = sys._getframe(3) t = threading.currentThread() if t.additional_info.is_tracing: return False diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 3865a436b80f..ead311b037d4 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -34,6 +34,7 @@ from _pydevd_bundle.pydevd_comm import CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT, CM set_global_debugger, WriterThread, pydevd_find_thread_by_id, pydevd_log, \ start_client, start_server, InternalGetBreakpointException, InternalSendCurrExceptionTrace, \ InternalSendCurrExceptionTraceProceeded +from _pydevd_bundle.pydevd_breakpointhook import install_breakpointhook from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads @@ -55,32 +56,6 @@ __version__ = '.'.join(__version_info_str__) #IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe - -def install_breakpointhook(pydevd_breakpointhook=None): - if pydevd_breakpointhook is None: - def pydevd_breakpointhook(*args, **kwargs): - hookname = os.getenv('PYTHONBREAKPOINT') - if hookname is not None and len(hookname) > 0 and hasattr(sys, '__breakpointhook__'): - sys.__breakpointhook__(*args, **kwargs) - else: - py_db = get_global_debugger() - if (py_db is not None) and (py_db.frame_eval_func is not None): - from _pydevd_frame_eval.pydevd_frame_tracing import suspend_at_builtin_breakpoint - suspend_at_builtin_breakpoint() - else: - settrace(*args, **kwargs) - - if sys.version_info[0:2] >= (3, 7): - # There are some choices on how to provide the breakpoint hook. Namely, we can provide a - # PYTHONBREAKPOINT which provides the import path for a method to be executed or we - # can override sys.breakpointhook. - # pydevd overrides sys.breakpointhook instead of providing an environment variable because - # it's possible that the debugger starts the user program but is not available in the - # PYTHONPATH (and would thus fail to be imported if PYTHONBREAKPOINT was set to pydevd.settrace). - # Note that the implementation still takes PYTHONBREAKPOINT in account (so, if it was provided - # by someone else, it'd still work). - sys.breakpointhook = pydevd_breakpointhook - # Install the breakpoint hook at import time. install_breakpointhook() @@ -1198,6 +1173,7 @@ def settrace( trace_only_current_thread=False, overwrite_prev_trace=False, patch_multiprocessing=False, + stop_at_frame=None, ): '''Sets the tracing function with the pydev debug function and initializes needed facilities. @@ -1221,18 +1197,22 @@ def settrace( @param patch_multiprocessing: if True we'll patch the functions which create new processes so that launched processes are debugged. + + @param stop_at_frame: if passed it'll stop at the given frame, otherwise it'll stop in the function which + called this method. ''' _set_trace_lock.acquire() try: _locked_settrace( - host, - stdoutToServer, - stderrToServer, - port, - suspend, - trace_only_current_thread, - overwrite_prev_trace, - patch_multiprocessing, + host, + stdoutToServer, + stderrToServer, + port, + suspend, + trace_only_current_thread, + overwrite_prev_trace, + patch_multiprocessing, + stop_at_frame, ) finally: _set_trace_lock.release() @@ -1250,6 +1230,7 @@ def _locked_settrace( trace_only_current_thread, overwrite_prev_trace, patch_multiprocessing, + stop_at_frame, ): if patch_multiprocessing: try: @@ -1334,16 +1315,11 @@ def _locked_settrace( PyDBCommandThread(debugger).start() CheckOutputThread(debugger).start() - #Suspend as the last thing after all tracing is in place. - if suspend: - debugger.set_suspend(t, CMD_THREAD_SUSPEND) - - else: # ok, we're already in debug mode, with all set, so, let's just set the break debugger = get_global_debugger() - debugger.set_trace_for_frame_and_parents(get_frame(), False) + debugger.set_trace_for_frame_and_parents(get_frame(), also_add_to_passed_frame=True, overwrite_prev_trace=True) t = threadingCurrentThread() try: @@ -1358,8 +1334,17 @@ def _locked_settrace( # Trace future threads? debugger.patch_threads() - - if suspend: + # Suspend as the last thing after all tracing is in place. + if suspend: + if stop_at_frame is not None: + # If the step was set we have to go to run state and + # set the proper frame for it to stop. + additional_info.pydev_state = STATE_RUN + additional_info.pydev_step_cmd = CMD_STEP_OVER + additional_info.pydev_step_stop = stop_at_frame + additional_info.suspend_type = PYTHON_SUSPEND + else: + # Ask to break as soon as possible. debugger.set_suspend(t, CMD_THREAD_SUSPEND)