Repository navigation
bdb.clear_all_file_breaks skips breakpoints when multiple breakpoints share a line #149015
Description
Bug report
Bug description:
bdb.Bdb.clear_all_file_breaks(filename) iterates over a list while mutating
it via Breakpoint.deleteMe(), which removes elements from the same list.
When two or more breakpoints exist at the same (filename, lineno), this
classic iterate-and-delete pattern skips alternate elements, leaving orphan
Breakpoint instances in Breakpoint.bplist and Breakpoint.bpbynumber.
The same module already uses the defensive slice-copy pattern in
clear_break() (the sibling function), so this is a clear local
inconsistency, not a contractual choice.
Internal asymmetry (smoking gun):
In Lib/bdb.py:
clear_break() — defensive copy:
for bp in Breakpoint.bplist[filename, lineno][:]:
bp.deleteMe()clear_all_file_breaks() — no copy:
for line in self.breaks[filename]:
blist = Breakpoint.bplist[filename, line]
for bp in blist: # iterating raw list
bp.deleteMe() # mutates blistBoth functions delete breakpoints via the same deleteMe() mechanism, which
removes from Breakpoint.bplist[(filename, lineno)]. The asymmetric defense
between sibling functions is the strongest evidence this is an oversight
rather than a deliberate design choice.
Reproducer:
import bdb
class MyDbg(bdb.Bdb): pass
dbg = MyDbg()
# Three breakpoints at the same (file, line)
dbg.set_break(__file__, 10)
dbg.set_break(__file__, 10)
dbg.set_break(__file__, 10)
# Snapshot before clearing
key = (bdb.canonic(__file__), 10)
before = list(bdb.Breakpoint.bplist.get(key, []))
print(f"Before: {len(before)} breakpoints in Breakpoint.bplist[{key}]")
# Clear all in this file
dbg.clear_all_file_breaks(bdb.canonic(__file__))
# Snapshot after clearing
after = list(bdb.Breakpoint.bplist.get(key, []))
print(f"After: {len(after)} breakpoints in Breakpoint.bplist[{key}]")
print(f"Orphans in Breakpoint.bpbynumber: "
f"{[n for n, b in enumerate(bdb.Breakpoint.bpbynumber) if b is not None]}")Expected behavior:
Before: 3 breakpoints in Breakpoint.bplist[...]
After: 0 breakpoints in Breakpoint.bplist[...]
Orphans in Breakpoint.bpbynumber: []
Actual behavior (Python 3.14.3):
Before: 3 breakpoints in Breakpoint.bplist[...]
After: 1 breakpoints in Breakpoint.bplist[...] ← orphan remains
Orphans in Breakpoint.bpbynumber: [<some index>] ← orphan remains
For N breakpoints at the same (file, line), floor(N/2) orphans remain.
Suggested fix (one-character diff):
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -755,7 +755,7 @@
if filename not in self.breaks:
return 'There are no breakpoints in %s' % filename
for line in self.breaks[filename]:
blist = Breakpoint.bplist[filename, line]
- for bp in blist:
+ for bp in blist[:]:
bp.deleteMe()
del self.breaks[filename]This matches the existing pattern at clear_break() in the same file.
Suggested test (Lib/test/test_bdb.py):
def test_clear_all_file_breaks_with_multiple_bps_same_line(self):
"""Regression test: clear_all_file_breaks must remove all breakpoints,
even when multiple breakpoints share the same (file, line)."""
dbg = bdb.Bdb()
src = bdb.canonic(__file__)
dbg.set_break(src, 10)
dbg.set_break(src, 10)
dbg.set_break(src, 10)
self.assertEqual(len(bdb.Breakpoint.bplist[(src, 10)]), 3)
dbg.clear_all_file_breaks(src)
self.assertNotIn((src, 10), bdb.Breakpoint.bplist)Versions:
- Reproduced on Python 3.14.3 (Windows, MINGW64).
- Reading source confirms identical pattern in
mainand 3.13 branches. - Behavior identical regardless of whether the breakpoints are set with
conditions, ignore counts, or via differentBdbsubclasses.
Related (not duplicate):
Issue #54770 (bpo-10561, fixed 2010) covers a different scenario: the
clear bpnumber pdb command potentially deleting more than one breakpoint.
That issue addresses clear_bpbynumber / breakpoint identity. The bug
reported here is in clear_all_file_breaks and concerns iterate-and-delete
on the shared Breakpoint.bplist list. No prior issue or PR specifically
targets this asymmetry between clear_break (defensive) and
clear_all_file_breaks (vulnerable).
Linked PRs
- gh-149015: avoid iterate-and-delete issue in
Bdb.clear_all_file_breaks#149039 - gh-149015: Fix iterate-and-delete issue in Bdb.clear_all_file_breaks #149843
- gh-149015: Fix clear_all_file_breaks for breakpoints sharing a line in bdb #159748
- [3.15] gh-149015: Fix clear_all_file_breaks for breakpoints sharing a line in bdb (GH-159748) #159801
- [3.14] gh-149015: Fix clear_all_file_breaks for breakpoints sharing a line in bdb (GH-159748) #159802
Activity
picnixz commentedon Apr 26, 2026
cc @gaogaotiantian
gaogaotiantian commentedon Apr 26, 2026
This makes sense. Do you want to submit a PR for it? I can do it too if you just prefer submitting the issue :)
gh-149015: Fix clear_all_file_breaks for breakpoints sharing a line i…
[3.15] gh-149015: Fix clear_all_file_breaks for breakpoints sharing a…
[3.14] gh-149015: Fix clear_all_file_breaks for breakpoints sharing a…