33import fnmatch
44import sys
55import os
6+ from inspect import CO_GENERATOR
67
78__all__ = ["BdbQuit" , "Bdb" , "Breakpoint" ]
89
@@ -75,24 +76,48 @@ def dispatch_call(self, frame, arg):
7576 if not (self .stop_here (frame ) or self .break_anywhere (frame )):
7677 # No need to trace this function
7778 return # None
79+ # Ignore call events in generator except when stepping.
80+ if self .stopframe and frame .f_code .co_flags & CO_GENERATOR :
81+ return self .trace_dispatch
7882 self .user_call (frame , arg )
7983 if self .quitting : raise BdbQuit
8084 return self .trace_dispatch
8185
8286 def dispatch_return (self , frame , arg ):
8387 if self .stop_here (frame ) or frame == self .returnframe :
88+ # Ignore return events in generator except when stepping.
89+ if self .stopframe and frame .f_code .co_flags & CO_GENERATOR :
90+ return self .trace_dispatch
8491 try :
8592 self .frame_returning = frame
8693 self .user_return (frame , arg )
8794 finally :
8895 self .frame_returning = None
8996 if self .quitting : raise BdbQuit
97+ # The user issued a 'next' or 'until' command.
98+ if self .stopframe is frame and self .stoplineno != - 1 :
99+ self ._set_stopinfo (None , None )
90100 return self .trace_dispatch
91101
92102 def dispatch_exception (self , frame , arg ):
93103 if self .stop_here (frame ):
104+ # When stepping with next/until/return in a generator frame, skip
105+ # the internal StopIteration exception (with no traceback)
106+ # triggered by a subiterator run with the 'yield from' statement.
107+ if not (frame .f_code .co_flags & CO_GENERATOR
108+ and arg [0 ] is StopIteration and arg [2 ] is None ):
109+ self .user_exception (frame , arg )
110+ if self .quitting : raise BdbQuit
111+ # Stop at the StopIteration or GeneratorExit exception when the user
112+ # has set stopframe in a generator by issuing a return command, or a
113+ # next/until command at the last statement in the generator before the
114+ # exception.
115+ elif (self .stopframe and frame is not self .stopframe
116+ and self .stopframe .f_code .co_flags & CO_GENERATOR
117+ and arg [0 ] in (StopIteration , GeneratorExit )):
94118 self .user_exception (frame , arg )
95119 if self .quitting : raise BdbQuit
120+
96121 return self .trace_dispatch
97122
98123 # Normally derived classes don't override the following
@@ -115,10 +140,8 @@ def stop_here(self, frame):
115140 if self .stoplineno == - 1 :
116141 return False
117142 return frame .f_lineno >= self .stoplineno
118- while frame is not None and frame is not self .stopframe :
119- if frame is self .botframe :
120- return True
121- frame = frame .f_back
143+ if not self .stopframe :
144+ return True
122145 return False
123146
124147 def break_here (self , frame ):
@@ -207,7 +230,10 @@ def set_next(self, frame):
207230
208231 def set_return (self , frame ):
209232 """Stop when returning from the given frame."""
210- self ._set_stopinfo (frame .f_back , frame )
233+ if frame .f_code .co_flags & CO_GENERATOR :
234+ self ._set_stopinfo (frame , None , - 1 )
235+ else :
236+ self ._set_stopinfo (frame .f_back , frame )
211237
212238 def set_trace (self , frame = None ):
213239 """Start debugging from `frame`.
0 commit comments