gh-120254: Add a `commands` argument to `pdb.set_trace` (#120255)

This commit is contained in:
Tian Gao 2024-09-24 12:52:15 -07:00 committed by GitHub
parent fc9e6bf53d
commit af8403a58d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 30 additions and 7 deletions

View File

@ -159,12 +159,15 @@ slightly different way:
is entered. is entered.
.. function:: set_trace(*, header=None) .. function:: set_trace(*, header=None, commands=None)
Enter the debugger at the calling stack frame. This is useful to hard-code Enter the debugger at the calling stack frame. This is useful to hard-code
a breakpoint at a given point in a program, even if the code is not a breakpoint at a given point in a program, even if the code is not
otherwise being debugged (e.g. when an assertion fails). If given, otherwise being debugged (e.g. when an assertion fails). If given,
*header* is printed to the console just before debugging begins. *header* is printed to the console just before debugging begins.
The *commands* argument, if given, is a list of commands to execute
when the debugger starts.
.. versionchanged:: 3.7 .. versionchanged:: 3.7
The keyword-only argument *header*. The keyword-only argument *header*.
@ -173,6 +176,9 @@ slightly different way:
:func:`set_trace` will enter the debugger immediately, rather than :func:`set_trace` will enter the debugger immediately, rather than
on the next line of code to be executed. on the next line of code to be executed.
.. versionadded:: 3.14
The *commands* argument.
.. function:: post_mortem(traceback=None) .. function:: post_mortem(traceback=None)
Enter post-mortem debugging of the given *traceback* object. If no Enter post-mortem debugging of the given *traceback* object. If no

View File

@ -389,11 +389,11 @@ class _OutputRedirectingPdb(pdb.Pdb):
# still use input() to get user input # still use input() to get user input
self.use_rawinput = 1 self.use_rawinput = 1
def set_trace(self, frame=None): def set_trace(self, frame=None, *, commands=None):
self.__debugger_used = True self.__debugger_used = True
if frame is None: if frame is None:
frame = sys._getframe().f_back frame = sys._getframe().f_back
pdb.Pdb.set_trace(self, frame) pdb.Pdb.set_trace(self, frame, commands=commands)
def set_continue(self): def set_continue(self):
# Calling set_continue unconditionally would break unit test # Calling set_continue unconditionally would break unit test

View File

@ -361,10 +361,14 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self._chained_exceptions = tuple() self._chained_exceptions = tuple()
self._chained_exception_index = 0 self._chained_exception_index = 0
def set_trace(self, frame=None): def set_trace(self, frame=None, *, commands=None):
Pdb._last_pdb_instance = self Pdb._last_pdb_instance = self
if frame is None: if frame is None:
frame = sys._getframe().f_back frame = sys._getframe().f_back
if commands is not None:
self.rcLines.extend(commands)
super().set_trace(frame) super().set_trace(frame)
def sigint_handler(self, signum, frame): def sigint_handler(self, signum, frame):
@ -2350,13 +2354,14 @@ def runcall(*args, **kwds):
""" """
return Pdb().runcall(*args, **kwds) return Pdb().runcall(*args, **kwds)
def set_trace(*, header=None): def set_trace(*, header=None, commands=None):
"""Enter the debugger at the calling stack frame. """Enter the debugger at the calling stack frame.
This is useful to hard-code a breakpoint at a given point in a This is useful to hard-code a breakpoint at a given point in a
program, even if the code is not otherwise being debugged (e.g. when program, even if the code is not otherwise being debugged (e.g. when
an assertion fails). If given, *header* is printed to the console an assertion fails). If given, *header* is printed to the console
just before debugging begins. just before debugging begins. *commands* is an optional list of
pdb commands to run when the debugger starts.
""" """
if Pdb._last_pdb_instance is not None: if Pdb._last_pdb_instance is not None:
pdb = Pdb._last_pdb_instance pdb = Pdb._last_pdb_instance
@ -2364,7 +2369,7 @@ def set_trace(*, header=None):
pdb = Pdb() pdb = Pdb()
if header is not None: if header is not None:
pdb.message(header) pdb.message(header)
pdb.set_trace(sys._getframe().f_back) pdb.set_trace(sys._getframe().f_back, commands=commands)
# Post-Mortem interface # Post-Mortem interface

View File

@ -901,6 +901,17 @@ def test_pdb_where_command():
(Pdb) continue (Pdb) continue
""" """
def test_pdb_commands_with_set_trace():
"""Test that commands can be passed to Pdb.set_trace()
>>> def test_function():
... x = 1
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace(commands=['p x', 'c'])
>>> test_function()
1
"""
# skip this test if sys.flags.no_site = True; # skip this test if sys.flags.no_site = True;
# exit() isn't defined unless there's a site module. # exit() isn't defined unless there's a site module.

View File

@ -0,0 +1 @@
Added ``commands`` argument to :func:`pdb.set_trace` which allows users to send debugger commands from the source file.