summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/bdb.py29
-rw-r--r--Lib/test/test_bdb.py47
-rw-r--r--Lib/test/test_pdb.py80
-rw-r--r--Misc/NEWS.d/next/Library/2020-08-28-23-07-53.bpo-24160.MSGnKr.rst1
4 files changed, 144 insertions, 13 deletions
diff --git a/Lib/bdb.py b/Lib/bdb.py
index b18a061..abb50c0 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -34,6 +34,8 @@ class Bdb:
self.fncache = {}
self.frame_returning = None
+ self._load_breaks()
+
def canonic(self, filename):
"""Return canonical form of filename.
@@ -365,6 +367,12 @@ class Bdb:
# Call self.get_*break*() to see the breakpoints or better
# for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
+ def _add_to_breaks(self, filename, lineno):
+ """Add breakpoint to breaks, if not already there."""
+ bp_linenos = self.breaks.setdefault(filename, [])
+ if lineno not in bp_linenos:
+ bp_linenos.append(lineno)
+
def set_break(self, filename, lineno, temporary=False, cond=None,
funcname=None):
"""Set a new breakpoint for filename:lineno.
@@ -377,12 +385,21 @@ class Bdb:
line = linecache.getline(filename, lineno)
if not line:
return 'Line %s:%d does not exist' % (filename, lineno)
- list = self.breaks.setdefault(filename, [])
- if lineno not in list:
- list.append(lineno)
+ self._add_to_breaks(filename, lineno)
bp = Breakpoint(filename, lineno, temporary, cond, funcname)
return None
+ def _load_breaks(self):
+ """Apply all breakpoints (set in other instances) to this one.
+
+ Populates this instance's breaks list from the Breakpoint class's
+ list, which can have breakpoints set by another Bdb instance. This
+ is necessary for interactive sessions to keep the breakpoints
+ active across multiple calls to run().
+ """
+ for (filename, lineno) in Breakpoint.bplist.keys():
+ self._add_to_breaks(filename, lineno)
+
def _prune_breaks(self, filename, lineno):
"""Prune breakpoints for filename:lineno.
@@ -681,6 +698,12 @@ class Breakpoint:
else:
self.bplist[file, line] = [self]
+ @staticmethod
+ def clearBreakpoints():
+ Breakpoint.next = 1
+ Breakpoint.bplist = {}
+ Breakpoint.bpbynumber = [None]
+
def deleteMe(self):
"""Delete the breakpoint from the list associated to a file:line.
diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py
index 71be069..398698c 100644
--- a/Lib/test/test_bdb.py
+++ b/Lib/test/test_bdb.py
@@ -74,9 +74,7 @@ class BdbNotExpectedError(BdbException): """Unexpected result."""
dry_run = 0
def reset_Breakpoint():
- _bdb.Breakpoint.next = 1
- _bdb.Breakpoint.bplist = {}
- _bdb.Breakpoint.bpbynumber = [None]
+ _bdb.Breakpoint.clearBreakpoints()
def info_breakpoints():
bp_list = [bp for bp in _bdb.Breakpoint.bpbynumber if bp]
@@ -951,6 +949,49 @@ class BreakpointTestCase(BaseTestCase):
with TracerRun(self) as tracer:
self.assertRaises(BdbError, tracer.runcall, tfunc_import)
+ def test_load_bps_from_previous_Bdb_instance(self):
+ reset_Breakpoint()
+ db1 = Bdb()
+ fname = db1.canonic(__file__)
+ db1.set_break(__file__, 1)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+
+ db2 = Bdb()
+ db2.set_break(__file__, 2)
+ db2.set_break(__file__, 3)
+ db2.set_break(__file__, 4)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [1, 2, 3, 4]})
+ db2.clear_break(__file__, 1)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
+
+ db3 = Bdb()
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
+ self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
+ db2.clear_break(__file__, 2)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
+ self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
+
+ db4 = Bdb()
+ db4.set_break(__file__, 5)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
+ self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
+ self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
+ reset_Breakpoint()
+
+ db5 = Bdb()
+ db5.set_break(__file__, 6)
+ self.assertEqual(db1.get_all_breaks(), {fname: [1]})
+ self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
+ self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
+ self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
+ self.assertEqual(db5.get_all_breaks(), {fname: [6]})
+
+
class RunTestCase(BaseTestCase):
"""Test run, runeval and set_trace."""
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index d5abc3f..98e2b93 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -213,6 +213,9 @@ def test_pdb_basic_commands():
BAZ
"""
+def reset_Breakpoint():
+ import bdb
+ bdb.Breakpoint.clearBreakpoints()
def test_pdb_breakpoint_commands():
"""Test basic commands related to breakpoints.
@@ -227,10 +230,7 @@ def test_pdb_breakpoint_commands():
First, need to clear bdb state that might be left over from previous tests.
Otherwise, the new breakpoints might get assigned different numbers.
- >>> from bdb import Breakpoint
- >>> Breakpoint.next = 1
- >>> Breakpoint.bplist = {}
- >>> Breakpoint.bpbynumber = [None]
+ >>> reset_Breakpoint()
Now test the breakpoint commands. NORMALIZE_WHITESPACE is needed because
the breakpoint list outputs a tab for the "stop only" and "ignore next"
@@ -323,6 +323,72 @@ def test_pdb_breakpoint_commands():
4
"""
+def test_pdb_breakpoints_preserved_across_interactive_sessions():
+ """Breakpoints are remembered between interactive sessions
+
+ >>> reset_Breakpoint()
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ ... 'import test.test_pdb',
+ ... 'break test.test_pdb.do_something',
+ ... 'break test.test_pdb.do_nothing',
+ ... 'break',
+ ... 'continue',
+ ... ]):
+ ... pdb.run('print()')
+ > <string>(1)<module>()
+ (Pdb) import test.test_pdb
+ (Pdb) break test.test_pdb.do_something
+ Breakpoint 1 at ...test_pdb.py:...
+ (Pdb) break test.test_pdb.do_nothing
+ Breakpoint 2 at ...test_pdb.py:...
+ (Pdb) break
+ Num Type Disp Enb Where
+ 1 breakpoint keep yes at ...test_pdb.py:...
+ 2 breakpoint keep yes at ...test_pdb.py:...
+ (Pdb) continue
+
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ ... 'break',
+ ... 'break pdb.find_function',
+ ... 'break',
+ ... 'clear 1',
+ ... 'continue',
+ ... ]):
+ ... pdb.run('print()')
+ > <string>(1)<module>()
+ (Pdb) break
+ Num Type Disp Enb Where
+ 1 breakpoint keep yes at ...test_pdb.py:...
+ 2 breakpoint keep yes at ...test_pdb.py:...
+ (Pdb) break pdb.find_function
+ Breakpoint 3 at ...pdb.py:94
+ (Pdb) break
+ Num Type Disp Enb Where
+ 1 breakpoint keep yes at ...test_pdb.py:...
+ 2 breakpoint keep yes at ...test_pdb.py:...
+ 3 breakpoint keep yes at ...pdb.py:...
+ (Pdb) clear 1
+ Deleted breakpoint 1 at ...test_pdb.py:...
+ (Pdb) continue
+
+ >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ ... 'break',
+ ... 'clear 2',
+ ... 'clear 3',
+ ... 'continue',
+ ... ]):
+ ... pdb.run('print()')
+ > <string>(1)<module>()
+ (Pdb) break
+ Num Type Disp Enb Where
+ 2 breakpoint keep yes at ...test_pdb.py:...
+ 3 breakpoint keep yes at ...pdb.py:...
+ (Pdb) clear 2
+ Deleted breakpoint 2 at ...test_pdb.py:...
+ (Pdb) clear 3
+ Deleted breakpoint 3 at ...pdb.py:...
+ (Pdb) continue
+ """
def do_nothing():
pass
@@ -699,8 +765,7 @@ def test_next_until_return_at_return_event():
... test_function_2()
... end = 1
- >>> from bdb import Breakpoint
- >>> Breakpoint.next = 1
+ >>> reset_Breakpoint()
>>> with PdbTestInput(['break test_function_2',
... 'continue',
... 'return',
@@ -1137,7 +1202,7 @@ def test_pdb_next_command_in_generator_for_loop():
> <doctest test.test_pdb.test_pdb_next_command_in_generator_for_loop[1]>(3)test_function()
-> for i in test_gen():
(Pdb) break test_gen
- Breakpoint 6 at <doctest test.test_pdb.test_pdb_next_command_in_generator_for_loop[0]>:1
+ Breakpoint 1 at <doctest test.test_pdb.test_pdb_next_command_in_generator_for_loop[0]>:1
(Pdb) continue
> <doctest test.test_pdb.test_pdb_next_command_in_generator_for_loop[0]>(2)test_gen()
-> yield 0
@@ -1213,6 +1278,7 @@ def test_pdb_issue_20766():
... print('pdb %d: %s' % (i, sess._previous_sigint_handler))
... i += 1
+ >>> reset_Breakpoint()
>>> with PdbTestInput(['continue',
... 'continue']):
... test_function()
diff --git a/Misc/NEWS.d/next/Library/2020-08-28-23-07-53.bpo-24160.MSGnKr.rst b/Misc/NEWS.d/next/Library/2020-08-28-23-07-53.bpo-24160.MSGnKr.rst
new file mode 100644
index 0000000..c0cfd87
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-08-28-23-07-53.bpo-24160.MSGnKr.rst
@@ -0,0 +1 @@
+Fixed bug where breakpoints did not persist across multiple debugger sessions in :mod:`pdb`'s interactive mode.