From 3cc57505e530c64b7d783e5ac15d3e66d233bbbb Mon Sep 17 00:00:00 2001 From: Hai Zhu <35182391+cocolato@users.noreply.github.com> Date: Sun, 21 Dec 2025 01:27:34 +0800 Subject: [PATCH] gh-142834: pdb commands command should use last available breakpoint (#142835) --- Doc/library/pdb.rst | 3 +- Lib/pdb.py | 9 +++- Lib/test/test_pdb.py | 43 +++++++++++++++++++ ...-12-16-15-32-41.gh-issue-142834.g7mHw_.rst | 1 + 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst index 0bbdc425352..8ab3e7ec9ef 100644 --- a/Doc/library/pdb.rst +++ b/Doc/library/pdb.rst @@ -520,7 +520,8 @@ can be overridden by the local file. To remove all commands from a breakpoint, type ``commands`` and follow it immediately with ``end``; that is, give no commands. - With no *bpnumber* argument, ``commands`` refers to the last breakpoint set. + With no *bpnumber* argument, ``commands`` refers to the most recently set + breakpoint that still exists. You can use breakpoint commands to start your program up again. Simply use the :pdbcmd:`continue` command, or :pdbcmd:`step`, diff --git a/Lib/pdb.py b/Lib/pdb.py index c1a5db080dc..4a6bc17e91c 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -1315,7 +1315,14 @@ def do_commands(self, arg): reached. """ if not arg: - bnum = len(bdb.Breakpoint.bpbynumber) - 1 + for bp in reversed(bdb.Breakpoint.bpbynumber): + if bp is None: + continue + bnum = bp.number + break + else: + self.error('cannot set commands: no existing breakpoint') + return else: try: bnum = int(arg) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5cba34ff8ba..4352aa6abfe 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3478,6 +3478,49 @@ def test_pdb_issue_gh_65052(): (Pdb) continue """ +def test_pdb_commands_last_breakpoint(): + """See GH-142834 + + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... foo = 1 + ... bar = 2 + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'break 4', + ... 'break 3', + ... 'clear 2', + ... 'commands', + ... 'p "success"', + ... 'end', + ... 'continue', + ... 'clear 1', + ... 'commands', + ... 'continue', + ... ]): + ... test_function() + > (2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) break 4 + Breakpoint 1 at :4 + (Pdb) break 3 + Breakpoint 2 at :3 + (Pdb) clear 2 + Deleted breakpoint 2 at :3 + (Pdb) commands + (com) p "success" + (com) end + (Pdb) continue + 'success' + > (4)test_function() + -> bar = 2 + (Pdb) clear 1 + Deleted breakpoint 1 at :4 + (Pdb) commands + *** cannot set commands: no existing breakpoint + (Pdb) continue + """ + @support.force_not_colorized_test_class @support.requires_subprocess() diff --git a/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst b/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst new file mode 100644 index 00000000000..8cde592e7c9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-16-15-32-41.gh-issue-142834.g7mHw_.rst @@ -0,0 +1 @@ +Change the :mod:`pdb` ``commands`` command to use the last available breakpoint instead of failing when the most recently created breakpoint was deleted.