gh-82987: Stop on calling frame unconditionally for inline breakpoints (#130493)

This commit is contained in:
Tian Gao 2025-03-04 11:35:47 -05:00 committed by GitHub
parent efadc5874c
commit 63b6ec31c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 3 deletions

View file

@ -245,6 +245,10 @@ access further features, you have to do this yourself:
.. versionadded:: 3.14
Added the *mode* argument.
.. versionchanged:: 3.14
Inline breakpoints like :func:`breakpoint` or :func:`pdb.set_trace` will
always stop the program at calling frame, ignoring the *skip* pattern (if any).
.. method:: run(statement, globals=None, locals=None)
runeval(expression, globals=None, locals=None)
runcall(function, *args, **kwds)

View file

@ -780,6 +780,11 @@ pdb
the quit and call :func:`sys.exit`, instead of raising :exc:`bdb.BdbQuit`.
(Contributed by Tian Gao in :gh:`124704`.)
* Inline breakpoints like :func:`breakpoint` or :func:`pdb.set_trace` will
always stop the program at calling frame, ignoring the ``skip`` pattern
(if any).
(Contributed by Tian Gao in :gh:`130493`.)
pickle
------

View file

@ -215,10 +215,13 @@ class Bdb:
If the debugger stops on the current opcode, invoke
self.user_opcode(). Raise BdbQuit if self.quitting is set.
Return self.trace_dispatch to continue tracing in this scope.
Opcode event will always trigger the user callback. For now the only
opcode event is from an inline set_trace() and we want to stop there
unconditionally.
"""
if self.stop_here(frame) or self.break_here(frame):
self.user_opcode(frame)
if self.quitting: raise BdbQuit
self.user_opcode(frame)
if self.quitting: raise BdbQuit
return self.trace_dispatch
# Normally derived classes don't override the following

View file

@ -4342,6 +4342,28 @@ class PdbTestInline(unittest.TestCase):
# The quit prompt should be printed exactly twice
self.assertEqual(stdout.count("Quit anyway"), 2)
def test_set_trace_with_skip(self):
"""GH-82897
Inline set_trace() should break unconditionally. This example is a
bit oversimplified, but as `pdb.set_trace()` uses the previous Pdb
instance, it's possible that we had a previous pdb instance with
skip values when we use `pdb.set_trace()` - it would be confusing
to users when such inline breakpoints won't break immediately.
"""
script = textwrap.dedent("""
import pdb
def foo():
x = 40 + 2
pdb.Pdb(skip=['__main__']).set_trace()
foo()
""")
commands = """
p x
c
"""
stdout, _ = self._run_script(script, commands)
self.assertIn("42", stdout)
@support.force_not_colorized_test_class
@support.requires_subprocess()

View file

@ -0,0 +1 @@
Inline breakpoints like :func:`breakpoint` or :func:`pdb.set_trace` will always stop the program at calling frame, ignoring the ``skip`` pattern (if any).