|
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_linenos = 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
|
@@ -273,9 +278,25 @@ def do_clear(self, arg):
|
273 | 278 | raise NotImplementedError("subclass of bdb must implement do_clear()")
|
274 | 279 |
|
275 | 280 | def break_anywhere(self, frame):
|
276 |
| - """Return True if there is any breakpoint for frame's filename. |
| 281 | + """Return True if there is any breakpoint in that frame |
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_linenos: |
| 298 | + self.code_linenos[code] = set(lineno for _, _, lineno in code.co_lines()) |
| 299 | + return lineno in self.code_linenos[code] |
279 | 300 |
|
280 | 301 | # Derived classes should override the user_* methods
|
281 | 302 | # to gain control.
|
@@ -360,7 +381,7 @@ def set_next(self, frame):
|
360 | 381 | def set_return(self, frame):
|
361 | 382 | """Stop when returning from the given frame."""
|
362 | 383 | if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
|
363 |
| - self._set_stopinfo(frame, None, -1) |
| 384 | + self._set_stopinfo(frame, frame, -1) |
364 | 385 | else:
|
365 | 386 | self._set_stopinfo(frame.f_back, frame)
|
366 | 387 |
|
|
0 commit comments