3232from _pydevd_bundle .pydevd_trace_dispatch import is_unhandled_exception
3333from _pydevd_bundle .pydevd_breakpoints import stop_on_unhandled_exception
3434from _pydevd_bundle .pydevd_utils import get_clsname_for_code
35+ from _pydevd_bundle .pydevd_dont_trace_files import PYDEV_FILE
36+
3537
3638# fmt: off
3739# IFDEF CYTHON
@@ -168,7 +170,6 @@ def _get_bootstrap_frame(depth: int) -> Tuple[Optional[FrameType], bool]:
168170
169171 return f_bootstrap , is_bootstrap_frame_internal
170172
171-
172173# fmt: off
173174# IFDEF CYTHON
174175# cdef _get_unhandled_exception_frame(int depth):
@@ -177,10 +178,19 @@ def _get_unhandled_exception_frame(depth: int) -> Optional[FrameType]:
177178# ENDIF
178179# fmt: on
179180 try :
180- return _thread_local_info .f_unhandled
181+ result = _thread_local_info .f_unhandled
182+
183+ # Make sure the result is from the same exception. That means the result is in the stack somewhere.
184+ if result is not None :
185+ orig = frame = _getframe (depth )
186+ while result != frame and frame is not None :
187+ frame = frame .f_back
188+ if frame is not None :
189+ return result
190+ del _thread_local_info .f_unhandled
191+ raise AttributeError ("Unhandled frame from different exception" )
181192 except :
182- frame = _getframe (depth )
183- f_unhandled = frame
193+ f_unhandled = _getframe (depth )
184194
185195 while f_unhandled is not None and f_unhandled .f_back is not None :
186196 f_back = f_unhandled .f_back
@@ -205,6 +215,14 @@ def _get_unhandled_exception_frame(depth: int) -> Optional[FrameType]:
205215 if f_back .f_code .co_name .startswith (("run" , "_run" )):
206216 break
207217
218+ elif name == "<frozen runpy>" :
219+ if f_back .f_code .co_name .startswith (("run" , "_run" )):
220+ break
221+
222+ elif name == "runpy" :
223+ if f_back .f_code .co_name .startswith (("run" , "_run" )):
224+ break
225+
208226 f_unhandled = f_back
209227
210228 if f_unhandled is not None :
@@ -631,6 +649,7 @@ def _enable_line_tracing(code):
631649# ENDIF
632650# fmt: on
633651 # print('enable line tracing', code)
652+ _ensure_monitoring ()
634653 events = monitor .get_local_events (DEBUGGER_ID , code )
635654 monitor .set_local_events (DEBUGGER_ID , code , events | monitor .events .LINE | monitor .events .JUMP )
636655
@@ -643,6 +662,7 @@ def _enable_return_tracing(code):
643662# ENDIF
644663# fmt: on
645664 # print('enable return tracing', code)
665+ _ensure_monitoring ()
646666 events = monitor .get_local_events (DEBUGGER_ID , code )
647667 monitor .set_local_events (DEBUGGER_ID , code , events | monitor .events .PY_RETURN )
648668
@@ -654,6 +674,7 @@ def _enable_return_tracing(code):
654674def disable_code_tracing (code ):
655675# ENDIF
656676# fmt: on
677+ _ensure_monitoring ()
657678 monitor .set_local_events (DEBUGGER_ID , code , 0 )
658679
659680
@@ -810,6 +831,7 @@ def _unwind_event(code, instruction, exc):
810831 if thread_info is None :
811832 return
812833
834+
813835 py_db : object = GlobalDebuggerHolder .global_dbg
814836 if py_db is None or py_db .pydb_disposed :
815837 return
@@ -831,9 +853,10 @@ def _unwind_event(code, instruction, exc):
831853 py_db .break_on_caught_exceptions or py_db .break_on_user_uncaught_exceptions or py_db .has_plugin_exception_breaks
832854 )
833855
856+
834857 if has_caught_exception_breakpoint_in_pydb :
835858 _should_stop , frame , user_uncaught_exc_info = should_stop_on_exception (
836- py_db , thread_info .additional_info , frame , thread_info .thread , arg , None
859+ py_db , thread_info .additional_info , frame , thread_info .thread , arg , None , is_unwind = True
837860 )
838861 if user_uncaught_exc_info :
839862 # TODO: Check: this may no longer be needed as in the unwind we know it's
@@ -842,16 +865,17 @@ def _unwind_event(code, instruction, exc):
842865 container_obj = _TryExceptContainerObj (py_db .collect_try_except_info (frame .f_code ))
843866 func_code_info .try_except_container_obj = container_obj
844867
845- if is_unhandled_exception (
868+ is_unhandled = is_unhandled_exception (
846869 func_code_info .try_except_container_obj , py_db , frame , user_uncaught_exc_info [1 ], user_uncaught_exc_info [2 ]
847- ):
848- # print('stop in user uncaught')
870+ )
871+
872+ if is_unhandled :
849873 handle_exception (py_db , thread_info .thread , frame , user_uncaught_exc_info [0 ], EXCEPTION_TYPE_USER_UNHANDLED )
850874 return
851875
852876 break_on_uncaught_exceptions = py_db .break_on_uncaught_exceptions
853877 if break_on_uncaught_exceptions :
854- if frame is _get_unhandled_exception_frame (depth = 1 ):
878+ if frame is _get_unhandled_exception_frame (1 ):
855879 stop_on_unhandled_exception (py_db , thread_info .thread , thread_info .additional_info , arg )
856880 return
857881
@@ -881,7 +905,7 @@ def _raise_event(code, instruction, exc):
881905 thread_info = _get_thread_info (True , 1 )
882906 if thread_info is None :
883907 return
884-
908+
885909 py_db : object = GlobalDebuggerHolder .global_dbg
886910 if py_db is None or py_db .pydb_disposed :
887911 return
@@ -895,17 +919,22 @@ def _raise_event(code, instruction, exc):
895919 if func_code_info .always_skip_code :
896920 return
897921
898- # print('_raise_event --- ', code, exc)
899-
900922 frame = _getframe (1 )
901923 arg = (type (exc ), exc , exc .__traceback__ )
924+
925+ # Compute the previous exception info (if any). We use it to check if the exception
926+ # should be stopped
927+ prev_exc_info = _thread_local_info ._user_uncaught_exc_info if hasattr (_thread_local_info , "_user_uncaught_exc_info" ) else None
902928 should_stop , frame , _user_uncaught_exc_info = should_stop_on_exception (
903- py_db , thread_info .additional_info , frame , thread_info .thread , arg , None
929+ py_db , thread_info .additional_info , frame , thread_info .thread , arg , prev_exc_info
904930 )
931+
932+ # Save the current exception info for the next raise event.
933+ _thread_local_info ._user_uncaught_exc_info = _user_uncaught_exc_info
934+
905935 # print('!!!! should_stop (in raise)', should_stop)
906936 if should_stop :
907937 handle_exception (py_db , thread_info .thread , frame , arg , EXCEPTION_TYPE_HANDLED )
908- return
909938
910939
911940# fmt: off
@@ -1307,6 +1336,10 @@ def _jump_event(code, from_offset, to_offset):
13071336 if py_db is None or py_db .pydb_disposed :
13081337 return monitor .DISABLE
13091338
1339+ # If we get another jump event, remove the extra check for the line event
1340+ if hasattr (_thread_local_info , "f_disable_next_line_if_match" ):
1341+ del _thread_local_info .f_disable_next_line_if_match
1342+
13101343 if not thread_info .trace or thread_info .thread ._is_stopped :
13111344 # For thread-related stuff we can't disable the code tracing because other
13121345 # threads may still want it...
@@ -1325,14 +1358,17 @@ def _jump_event(code, from_offset, to_offset):
13251358
13261359 from_line = func_code_info .get_line_of_offset (from_offset )
13271360 to_line = func_code_info .get_line_of_offset (to_offset )
1328- # print('jump event', code.co_name, 'from line', from_line, 'to line', to_line)
13291361
13301362 if from_line != to_line :
13311363 # I.e.: use case: "yield from [j for j in a if j % 2 == 0]"
13321364 return monitor .DISABLE
13331365
13341366 # We know the frame depth.
13351367 frame = _getframe (1 )
1368+
1369+ # Disable the next line event as we're jumping to a line. The line event will be redundant.
1370+ _thread_local_info .f_disable_next_line_if_match = (func_code_info .co_filename , frame .f_lineno )
1371+
13361372 return _internal_line_event (func_code_info , frame , frame .f_lineno )
13371373
13381374
@@ -1360,6 +1396,15 @@ def _line_event(code, line):
13601396 if py_db is None or py_db .pydb_disposed :
13611397 return monitor .DISABLE
13621398
1399+ # If we get another line event, remove the extra check for the line event
1400+ if hasattr (_thread_local_info , "f_disable_next_line_if_match" ):
1401+ (co_filename , line_to_skip ) = _thread_local_info .f_disable_next_line_if_match
1402+ del _thread_local_info .f_disable_next_line_if_match
1403+ if line_to_skip is line and co_filename == code .co_filename :
1404+ # The last jump already jumped to this line and we haven't had any
1405+ # line events or jumps since then. We don't want to consider this line twice
1406+ return
1407+
13631408 if not thread_info .trace or thread_info .thread ._is_stopped :
13641409 # For thread-related stuff we can't disable the code tracing because other
13651410 # threads may still want it...
@@ -1604,6 +1649,7 @@ def _start_method_event(code, instruction_offset):
16041649 # threads may still want it...
16051650 return
16061651
1652+
16071653 frame = _getframe (1 )
16081654 func_code_info = _get_func_code_info (code , frame )
16091655 if func_code_info .always_skip_code :
@@ -1651,6 +1697,19 @@ def _start_method_event(code, instruction_offset):
16511697
16521698 return monitor .DISABLE
16531699
1700+ # fmt: off
1701+ # IFDEF CYTHON
1702+ # cpdef _ensure_monitoring():
1703+ # ELSE
1704+ def _ensure_monitoring ():
1705+ # ENDIF
1706+ # fmt: on
1707+ DEBUGGER_ID = monitor .DEBUGGER_ID
1708+ if not monitor .get_tool (DEBUGGER_ID ):
1709+ monitor .use_tool_id (DEBUGGER_ID , "pydevd" )
1710+ update_monitor_events ()
1711+ restart_events ()
1712+
16541713
16551714# fmt: off
16561715# IFDEF CYTHON
0 commit comments