diff options
-rw-r--r-- | Lib/bdb.py | 29 | ||||
-rw-r--r-- | Lib/test/test_bdb.py | 47 | ||||
-rw-r--r-- | Lib/test/test_pdb.py | 80 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2020-08-28-23-07-53.bpo-24160.MSGnKr.rst | 1 |
4 files changed, 144 insertions, 13 deletions
@@ -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. |