gh-124400: Use the normal command path for breakpoint commands (#124401)

Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
This commit is contained in:
Tian Gao 2024-09-29 16:46:16 -07:00 committed by GitHub
parent 4b83c03ce9
commit b5774603a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 77 additions and 40 deletions

View file

@ -439,17 +439,20 @@ can be overridden by the local file.
Specifying any command resuming execution Specifying any command resuming execution
(currently :pdbcmd:`continue`, :pdbcmd:`step`, :pdbcmd:`next`, (currently :pdbcmd:`continue`, :pdbcmd:`step`, :pdbcmd:`next`,
:pdbcmd:`return`, :pdbcmd:`jump`, :pdbcmd:`quit` and their abbreviations) :pdbcmd:`return`, :pdbcmd:`until`, :pdbcmd:`jump`, :pdbcmd:`quit` and their abbreviations)
terminates the command list (as if terminates the command list (as if
that command was immediately followed by end). This is because any time you that command was immediately followed by end). This is because any time you
resume execution (even with a simple next or step), you may encounter another resume execution (even with a simple next or step), you may encounter another
breakpoint—which could have its own command list, leading to ambiguities about breakpoint—which could have its own command list, leading to ambiguities about
which list to execute. which list to execute.
If you use the ``silent`` command in the command list, the usual message about If the list of commands contains the ``silent`` command, or a command that
stopping at a breakpoint is not printed. This may be desirable for breakpoints resumes execution, then the breakpoint message containing information about
that are to print a specific message and then continue. If none of the other the frame is not displayed.
commands print anything, you see no sign that the breakpoint was reached.
.. versionchanged:: 3.14
Frame information will not be displayed if a command that resumes execution
is present in the command list.
.. pdbcommand:: s(tep) .. pdbcommand:: s(tep)

View file

@ -350,10 +350,6 @@ class Pdb(bdb.Bdb, cmd.Cmd):
pass pass
self.commands = {} # associates a command list to breakpoint numbers self.commands = {} # associates a command list to breakpoint numbers
self.commands_doprompt = {} # for each bp num, tells if the prompt
# must be disp. after execing the cmd list
self.commands_silent = {} # for each bp num, tells if the stack trace
# must be disp. after execing the cmd list
self.commands_defining = False # True while in the process of defining self.commands_defining = False # True while in the process of defining
# a command list # a command list
self.commands_bnum = None # The breakpoint number for which we are self.commands_bnum = None # The breakpoint number for which we are
@ -437,8 +433,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
or frame.f_lineno <= 0): or frame.f_lineno <= 0):
return return
self._wait_for_mainpyfile = False self._wait_for_mainpyfile = False
if self.bp_commands(frame): self.bp_commands(frame)
self.interaction(frame, None) self.interaction(frame, None)
user_opcode = user_line user_opcode = user_line
@ -453,18 +449,9 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.currentbp in self.commands: self.currentbp in self.commands:
currentbp = self.currentbp currentbp = self.currentbp
self.currentbp = 0 self.currentbp = 0
lastcmd_back = self.lastcmd
self.setup(frame, None)
for line in self.commands[currentbp]: for line in self.commands[currentbp]:
self.onecmd(line) self.cmdqueue.append(line)
self.lastcmd = lastcmd_back self.cmdqueue.append(f'_pdbcmd_restore_lastcmd {self.lastcmd}')
if not self.commands_silent[currentbp]:
self.print_stack_entry(self.stack[self.curindex])
if self.commands_doprompt[currentbp]:
self._cmdloop()
self.forget()
return
return 1
def user_return(self, frame, return_value): def user_return(self, frame, return_value):
"""This function is called when a return trap is set here.""" """This function is called when a return trap is set here."""
@ -863,15 +850,15 @@ class Pdb(bdb.Bdb, cmd.Cmd):
cmd, arg, line = self.parseline(line) cmd, arg, line = self.parseline(line)
if not cmd: if not cmd:
return False return False
if cmd == 'silent': if cmd == 'end':
self.commands_silent[self.commands_bnum] = True
return False # continue to handle other cmd def in the cmd list
elif cmd == 'end':
return True # end of cmd list return True # end of cmd list
elif cmd == 'EOF': elif cmd == 'EOF':
print('') print('')
return True # end of cmd list return True # end of cmd list
cmdlist = self.commands[self.commands_bnum] cmdlist = self.commands[self.commands_bnum]
if cmd == 'silent':
cmdlist.append('_pdbcmd_silence_frame_status')
return False # continue to handle other cmd def in the cmd list
if arg: if arg:
cmdlist.append(cmd+' '+arg) cmdlist.append(cmd+' '+arg)
else: else:
@ -883,7 +870,6 @@ class Pdb(bdb.Bdb, cmd.Cmd):
func = self.default func = self.default
# one of the resuming commands # one of the resuming commands
if func.__name__ in self.commands_resuming: if func.__name__ in self.commands_resuming:
self.commands_doprompt[self.commands_bnum] = False
return True return True
return False return False
@ -996,6 +982,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.print_stack_trace(0) self.print_stack_trace(0)
self._show_display() self._show_display()
def _pdbcmd_silence_frame_status(self, arg):
if self.cmdqueue and self.cmdqueue[-1] == '_pdbcmd_print_frame_status':
self.cmdqueue.pop()
def _pdbcmd_restore_lastcmd(self, arg):
self.lastcmd = arg
# Command definitions, called by cmdloop() # Command definitions, called by cmdloop()
# The argument is the remaining string on the command line # The argument is the remaining string on the command line
# Return true to exit from the command loop # Return true to exit from the command loop
@ -1054,14 +1047,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.commands_bnum = bnum self.commands_bnum = bnum
# Save old definitions for the case of a keyboard interrupt. # Save old definitions for the case of a keyboard interrupt.
if bnum in self.commands: if bnum in self.commands:
old_command_defs = (self.commands[bnum], old_commands = self.commands[bnum]
self.commands_doprompt[bnum],
self.commands_silent[bnum])
else: else:
old_command_defs = None old_commands = None
self.commands[bnum] = [] self.commands[bnum] = []
self.commands_doprompt[bnum] = True
self.commands_silent[bnum] = False
prompt_back = self.prompt prompt_back = self.prompt
self.prompt = '(com) ' self.prompt = '(com) '
@ -1070,14 +1059,10 @@ class Pdb(bdb.Bdb, cmd.Cmd):
self.cmdloop() self.cmdloop()
except KeyboardInterrupt: except KeyboardInterrupt:
# Restore old definitions. # Restore old definitions.
if old_command_defs: if old_commands:
self.commands[bnum] = old_command_defs[0] self.commands[bnum] = old_commands
self.commands_doprompt[bnum] = old_command_defs[1]
self.commands_silent[bnum] = old_command_defs[2]
else: else:
del self.commands[bnum] del self.commands[bnum]
del self.commands_doprompt[bnum]
del self.commands_silent[bnum]
self.error('command definition aborted, old commands restored') self.error('command definition aborted, old commands restored')
finally: finally:
self.commands_defining = False self.commands_defining = False
@ -2093,7 +2078,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
# List of all the commands making the program resume execution. # List of all the commands making the program resume execution.
commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return', commands_resuming = ['do_continue', 'do_step', 'do_next', 'do_return',
'do_quit', 'do_jump'] 'do_until', 'do_quit', 'do_jump']
# Print a traceback starting at the top stack frame. # Print a traceback starting at the top stack frame.
# The most recently entered frame is printed last; # The most recently entered frame is printed last;

View file

@ -363,6 +363,54 @@ def test_pdb_breakpoint_commands():
4 4
""" """
def test_pdb_commands():
"""Test the commands command of pdb.
>>> def test_function():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... print(1)
... print(2)
... print(3)
>>> reset_Breakpoint()
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
... 'b 3',
... 'commands',
... 'silent', # suppress the frame status output
... 'p "hello"',
... 'end',
... 'b 4',
... 'commands',
... 'until 5', # no output, should stop at line 5
... 'continue', # hit breakpoint at line 3
... '', # repeat continue, hit breakpoint at line 4 then `until` to line 5
... '',
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_commands[0]>(2)test_function()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) b 3
Breakpoint 1 at <doctest test.test_pdb.test_pdb_commands[0]>:3
(Pdb) commands
(com) silent
(com) p "hello"
(com) end
(Pdb) b 4
Breakpoint 2 at <doctest test.test_pdb.test_pdb_commands[0]>:4
(Pdb) commands
(com) until 5
(Pdb) continue
'hello'
(Pdb)
1
2
> <doctest test.test_pdb.test_pdb_commands[0]>(5)test_function()
-> print(3)
(Pdb)
3
"""
def test_pdb_breakpoint_with_filename(): def test_pdb_breakpoint_with_filename():
"""Breakpoints with filename:lineno """Breakpoints with filename:lineno

View file

@ -0,0 +1 @@
Fixed a :mod:`pdb` bug where ``until`` has no effect when it appears in a ``commands`` sequence. Also avoid printing the frame information at a breakpoint that has a command list containing a command that resumes execution.