Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -423,17 +423,20 @@ can be overridden by the local file.

Specifying any command resuming execution
(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
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
breakpoint—which could have its own command list, leading to ambiguities about
which list to execute.

If you use the ``silent`` command in the command list, the usual message about
stopping at a breakpoint is not printed. This may be desirable for breakpoints
that are to print a specific message and then continue. If none of the other
commands print anything, you see no sign that the breakpoint was reached.
If the list of commands contains the ``silent`` command, or a command that
resumes execution, then the breakpoint message containing information about
the frame is not displayed.

.. versionchanged:: 3.14
Frame information will not be displayed if a command that resumes execution
is present in the command list.

.. pdbcommand:: s(tep)

Expand Down
55 changes: 20 additions & 35 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,6 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
pass

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
# a command list
self.commands_bnum = None # The breakpoint number for which we are
Expand Down Expand Up @@ -439,8 +435,8 @@ def user_line(self, frame):
or frame.f_lineno <= 0):
return
self._wait_for_mainpyfile = False
if self.bp_commands(frame):
self.interaction(frame, None)
self.bp_commands(frame)
self.interaction(frame, None)

user_opcode = user_line

Expand All @@ -455,18 +451,9 @@ def bp_commands(self, frame):
self.currentbp in self.commands:
currentbp = self.currentbp
self.currentbp = 0
lastcmd_back = self.lastcmd
self.setup(frame, None)
for line in self.commands[currentbp]:
self.onecmd(line)
self.lastcmd = lastcmd_back
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
self.cmdqueue.append(line)
self.cmdqueue.append(f'_pdbcmd_restore_lastcmd {self.lastcmd}')

def user_return(self, frame, return_value):
"""This function is called when a return trap is set here."""
Expand Down Expand Up @@ -865,15 +852,15 @@ def handle_command_def(self, line):
cmd, arg, line = self.parseline(line)
if not cmd:
return False
if cmd == 'silent':
self.commands_silent[self.commands_bnum] = True
return False # continue to handle other cmd def in the cmd list
elif cmd == 'end':
if cmd == 'end':
return True # end of cmd list
elif cmd == 'EOF':
print('')
return True # end of cmd list
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:
cmdlist.append(cmd+' '+arg)
else:
Expand All @@ -885,7 +872,6 @@ def handle_command_def(self, line):
func = self.default
# one of the resuming commands
if func.__name__ in self.commands_resuming:
self.commands_doprompt[self.commands_bnum] = False
return True
return False

Expand Down Expand Up @@ -998,6 +984,13 @@ def _pdbcmd_print_frame_status(self, arg):
self.print_stack_trace(0)
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()
# The argument is the remaining string on the command line
# Return true to exit from the command loop
Expand Down Expand Up @@ -1056,14 +1049,10 @@ def do_commands(self, arg):
self.commands_bnum = bnum
# Save old definitions for the case of a keyboard interrupt.
if bnum in self.commands:
old_command_defs = (self.commands[bnum],
self.commands_doprompt[bnum],
self.commands_silent[bnum])
old_commands = self.commands[bnum]
else:
old_command_defs = None
old_commands = None
self.commands[bnum] = []
self.commands_doprompt[bnum] = True
self.commands_silent[bnum] = False

prompt_back = self.prompt
self.prompt = '(com) '
Expand All @@ -1072,14 +1061,10 @@ def do_commands(self, arg):
self.cmdloop()
except KeyboardInterrupt:
# Restore old definitions.
if old_command_defs:
self.commands[bnum] = old_command_defs[0]
self.commands_doprompt[bnum] = old_command_defs[1]
self.commands_silent[bnum] = old_command_defs[2]
if old_commands:
self.commands[bnum] = old_commands
else:
del self.commands[bnum]
del self.commands_doprompt[bnum]
del self.commands_silent[bnum]
self.error('command definition aborted, old commands restored')
finally:
self.commands_defining = False
Expand Down Expand Up @@ -2091,7 +2076,7 @@ def complete_unalias(self, text, line, begidx, endidx):

# List of all the commands making the program resume execution.
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.
# The most recently entered frame is printed last;
Expand Down
48 changes: 48 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,54 @@ def test_pdb_breakpoint_commands():
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():
"""Breakpoints with filename:lineno

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading