mirror of https://github.com/python/cpython
gh-124552 : Improve the accuracy of possible breakpoint check in bdb (#124553)
This commit is contained in:
parent
2d8b6a4e9d
commit
adfe7657a3
27
Lib/bdb.py
27
Lib/bdb.py
|
@ -3,6 +3,7 @@
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import weakref
|
||||||
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
|
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
|
||||||
|
|
||||||
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
|
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
|
||||||
|
@ -36,6 +37,7 @@ class Bdb:
|
||||||
self.frame_returning = None
|
self.frame_returning = None
|
||||||
self.trace_opcodes = False
|
self.trace_opcodes = False
|
||||||
self.enterframe = None
|
self.enterframe = None
|
||||||
|
self.code_linenos = weakref.WeakKeyDictionary()
|
||||||
|
|
||||||
self._load_breaks()
|
self._load_breaks()
|
||||||
|
|
||||||
|
@ -155,6 +157,9 @@ class Bdb:
|
||||||
if self.stop_here(frame) or frame == self.returnframe:
|
if self.stop_here(frame) or frame == self.returnframe:
|
||||||
# Ignore return events in generator except when stepping.
|
# Ignore return events in generator except when stepping.
|
||||||
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
|
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
|
||||||
|
# It's possible to trigger a StopIteration exception in
|
||||||
|
# the caller so we must set the trace function in the caller
|
||||||
|
self._set_caller_tracefunc(frame)
|
||||||
return self.trace_dispatch
|
return self.trace_dispatch
|
||||||
try:
|
try:
|
||||||
self.frame_returning = frame
|
self.frame_returning = frame
|
||||||
|
@ -273,9 +278,25 @@ class Bdb:
|
||||||
raise NotImplementedError("subclass of bdb must implement do_clear()")
|
raise NotImplementedError("subclass of bdb must implement do_clear()")
|
||||||
|
|
||||||
def break_anywhere(self, frame):
|
def break_anywhere(self, frame):
|
||||||
"""Return True if there is any breakpoint for frame's filename.
|
"""Return True if there is any breakpoint in that frame
|
||||||
"""
|
"""
|
||||||
return self.canonic(frame.f_code.co_filename) in self.breaks
|
filename = self.canonic(frame.f_code.co_filename)
|
||||||
|
if filename not in self.breaks:
|
||||||
|
return False
|
||||||
|
for lineno in self.breaks[filename]:
|
||||||
|
if self._lineno_in_frame(lineno, frame):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _lineno_in_frame(self, lineno, frame):
|
||||||
|
"""Return True if the line number is in the frame's code object.
|
||||||
|
"""
|
||||||
|
code = frame.f_code
|
||||||
|
if lineno < code.co_firstlineno:
|
||||||
|
return False
|
||||||
|
if code not in self.code_linenos:
|
||||||
|
self.code_linenos[code] = set(lineno for _, _, lineno in code.co_lines())
|
||||||
|
return lineno in self.code_linenos[code]
|
||||||
|
|
||||||
# Derived classes should override the user_* methods
|
# Derived classes should override the user_* methods
|
||||||
# to gain control.
|
# to gain control.
|
||||||
|
@ -360,7 +381,7 @@ class Bdb:
|
||||||
def set_return(self, frame):
|
def set_return(self, frame):
|
||||||
"""Stop when returning from the given frame."""
|
"""Stop when returning from the given frame."""
|
||||||
if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
|
if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
|
||||||
self._set_stopinfo(frame, None, -1)
|
self._set_stopinfo(frame, frame, -1)
|
||||||
else:
|
else:
|
||||||
self._set_stopinfo(frame.f_back, frame)
|
self._set_stopinfo(frame.f_back, frame)
|
||||||
|
|
||||||
|
|
|
@ -518,6 +518,43 @@ def test_pdb_breakpoints_preserved_across_interactive_sessions():
|
||||||
(Pdb) continue
|
(Pdb) continue
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def test_pdb_break_anywhere():
|
||||||
|
"""Test break_anywhere() method of Pdb.
|
||||||
|
|
||||||
|
>>> def outer():
|
||||||
|
... def inner():
|
||||||
|
... import pdb
|
||||||
|
... import sys
|
||||||
|
... p = pdb.Pdb(nosigint=True, readrc=False)
|
||||||
|
... p.set_trace()
|
||||||
|
... frame = sys._getframe()
|
||||||
|
... print(p.break_anywhere(frame)) # inner
|
||||||
|
... print(p.break_anywhere(frame.f_back)) # outer
|
||||||
|
... print(p.break_anywhere(frame.f_back.f_back)) # caller
|
||||||
|
... inner()
|
||||||
|
|
||||||
|
>>> def caller():
|
||||||
|
... outer()
|
||||||
|
|
||||||
|
>>> def test_function():
|
||||||
|
... caller()
|
||||||
|
|
||||||
|
>>> reset_Breakpoint()
|
||||||
|
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
|
||||||
|
... 'b 3',
|
||||||
|
... 'c',
|
||||||
|
... ]):
|
||||||
|
... test_function()
|
||||||
|
> <doctest test.test_pdb.test_pdb_break_anywhere[0]>(6)inner()
|
||||||
|
-> p.set_trace()
|
||||||
|
(Pdb) b 3
|
||||||
|
Breakpoint 1 at <doctest test.test_pdb.test_pdb_break_anywhere[0]>:3
|
||||||
|
(Pdb) c
|
||||||
|
True
|
||||||
|
False
|
||||||
|
False
|
||||||
|
"""
|
||||||
|
|
||||||
def test_pdb_pp_repr_exc():
|
def test_pdb_pp_repr_exc():
|
||||||
"""Test that do_p/do_pp do not swallow exceptions.
|
"""Test that do_p/do_pp do not swallow exceptions.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Improve the accuracy of :mod:`bdb`'s check for the possibility of breakpoint in a frame. This makes it possible to disable unnecessary events in functions.
|
Loading…
Reference in New Issue