|
3 | 3 | import fnmatch |
4 | 4 | import sys |
5 | 5 | import os |
| 6 | +import weakref |
6 | 7 | from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR |
7 | 8 |
|
8 | 9 | __all__ = ["BdbQuit", "Bdb", "Breakpoint"] |
@@ -36,6 +37,7 @@ def __init__(self, skip=None): |
36 | 37 | self.frame_returning = None |
37 | 38 | self.trace_opcodes = False |
38 | 39 | self.enterframe = None |
| 40 | + self.code_lineno = weakref.WeakKeyDictionary() |
39 | 41 |
|
40 | 42 | self._load_breaks() |
41 | 43 |
|
@@ -155,6 +157,9 @@ def dispatch_return(self, frame, arg): |
155 | 157 | if self.stop_here(frame) or frame == self.returnframe: |
156 | 158 | # Ignore return events in generator except when stepping. |
157 | 159 | if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: |
| 160 | + # It's possible to trigger a StopIteration exception in |
| 161 | + # the caller so we must set the trace function in the caller |
| 162 | + self._set_caller_tracefunc(frame) |
158 | 163 | return self.trace_dispatch |
159 | 164 | try: |
160 | 165 | self.frame_returning = frame |
@@ -275,7 +280,25 @@ def do_clear(self, arg): |
275 | 280 | def break_anywhere(self, frame): |
276 | 281 | """Return True if there is any breakpoint for frame's filename. |
277 | 282 | """ |
278 | | - return self.canonic(frame.f_code.co_filename) in self.breaks |
| 283 | + filename = self.canonic(frame.f_code.co_filename) |
| 284 | + if filename not in self.breaks: |
| 285 | + return False |
| 286 | + for lineno in self.breaks[filename]: |
| 287 | + if self.lineno_in_frame(lineno, frame): |
| 288 | + return True |
| 289 | + return False |
| 290 | + |
| 291 | + def lineno_in_frame(self, lineno, frame): |
| 292 | + """Return True if the line number is in the frame's code object. |
| 293 | + """ |
| 294 | + code = frame.f_code |
| 295 | + if lineno < code.co_firstlineno: |
| 296 | + return False |
| 297 | + if code not in self.code_lineno: |
| 298 | + self.code_lineno[code] = set() |
| 299 | + for _, _, lineno in code.co_lines(): |
| 300 | + self.code_lineno[code].add(lineno) |
| 301 | + return lineno in self.code_lineno[frame.f_code] |
279 | 302 |
|
280 | 303 | # Derived classes should override the user_* methods |
281 | 304 | # to gain control. |
@@ -360,7 +383,7 @@ def set_next(self, frame): |
360 | 383 | def set_return(self, frame): |
361 | 384 | """Stop when returning from the given frame.""" |
362 | 385 | if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: |
363 | | - self._set_stopinfo(frame, None, -1) |
| 386 | + self._set_stopinfo(frame, frame, -1) |
364 | 387 | else: |
365 | 388 | self._set_stopinfo(frame.f_back, frame) |
366 | 389 |
|
|
0 commit comments