]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-121450: Make inline breakpoints use the most recent pdb instance (#121451)
authorTian Gao <gaogaotiantian@hotmail.com>
Thu, 11 Jul 2024 02:54:27 +0000 (19:54 -0700)
committerGitHub <noreply@github.com>
Thu, 11 Jul 2024 02:54:27 +0000 (19:54 -0700)
Doc/whatsnew/3.14.rst
Lib/bdb.py
Lib/pdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst [new file with mode: 0644]

index d02c10ec9cf3f342fc6b4d7d5eeb1307e887400d..da9b45cd8e58b38325f0b1107e024c62e59f842e 100644 (file)
@@ -114,6 +114,16 @@ pathlib
   another.
   (Contributed by Barney Gale in :gh:`73991`.)
 
+pdb
+---
+
+* Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now
+  reuse the most recent :class:`~pdb.Pdb` instance that calls
+  :meth:`~pdb.Pdb.set_trace()`, instead of creating a new one each time.
+  As a result, all the instance specific data like :pdbcmd:`display` and
+  :pdbcmd:`commands` are preserved across hard-coded breakpoints.
+  (Contributed by Tian Gao in :gh:`121450`.)
+
 symtable
 --------
 
index aa621053cfb4bcfaffc4f6321ca331992ecc1cb3..d7543017940d4fb378dc0e6e9b5bbe9b475bbe83 100644 (file)
@@ -369,6 +369,7 @@ class Bdb:
 
         If frame is not specified, debugging starts from caller's frame.
         """
+        sys.settrace(None)
         if frame is None:
             frame = sys._getframe().f_back
         self.reset()
index 85a3aa2e37996fd47d1e40e4a922e82fa1e924f0..7ff973149b167b5ff076785b25e8bac5d01a2f5a 100644 (file)
@@ -306,6 +306,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
 
     _file_mtime_table = {}
 
+    _last_pdb_instance = None
+
     def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
                  nosigint=False, readrc=True):
         bdb.Bdb.__init__(self, skip=skip)
@@ -359,6 +361,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
         self._chained_exceptions = tuple()
         self._chained_exception_index = 0
 
+    def set_trace(self, frame=None):
+        Pdb._last_pdb_instance = self
+        if frame is None:
+            frame = sys._getframe().f_back
+        super().set_trace(frame)
+
     def sigint_handler(self, signum, frame):
         if self.allow_kbdint:
             raise KeyboardInterrupt
@@ -2350,7 +2358,10 @@ def set_trace(*, header=None):
     an assertion fails). If given, *header* is printed to the console
     just before debugging begins.
     """
-    pdb = Pdb()
+    if Pdb._last_pdb_instance is not None:
+        pdb = Pdb._last_pdb_instance
+    else:
+        pdb = Pdb()
     if header is not None:
         pdb.message(header)
     pdb.set_trace(sys._getframe().f_back)
index 5c7445574f5d75f8e6c2cbe6d2071d53be9b76b5..343e15a4edc14c2bcab500197f82d82e5887e00f 100644 (file)
@@ -2448,6 +2448,49 @@ def test_pdb_show_attribute_and_item():
     (Pdb) c
     """
 
+# doctest will modify pdb.set_trace during the test, so we need to backup
+# the original function to use it in the test
+original_pdb_settrace = pdb.set_trace
+
+def test_pdb_with_inline_breakpoint():
+    """Hard-coded breakpoint() calls should invoke the same debugger instance
+
+    >>> def test_function():
+    ...     x = 1
+    ...     import pdb; pdb.Pdb().set_trace()
+    ...     original_pdb_settrace()
+    ...     x = 2
+
+    >>> with PdbTestInput(['display x',
+    ...                    'n',
+    ...                    'n',
+    ...                    'n',
+    ...                    'n',
+    ...                    'undisplay',
+    ...                    'c']):
+    ...     test_function()
+    > <doctest test.test_pdb.test_pdb_with_inline_breakpoint[0]>(3)test_function()
+    -> import pdb; pdb.Pdb().set_trace()
+    (Pdb) display x
+    display x: 1
+    (Pdb) n
+    > <doctest test.test_pdb.test_pdb_with_inline_breakpoint[0]>(4)test_function()
+    -> original_pdb_settrace()
+    (Pdb) n
+    > <doctest test.test_pdb.test_pdb_with_inline_breakpoint[0]>(4)test_function()
+    -> original_pdb_settrace()
+    (Pdb) n
+    > <doctest test.test_pdb.test_pdb_with_inline_breakpoint[0]>(5)test_function()
+    -> x = 2
+    (Pdb) n
+    --Return--
+    > <doctest test.test_pdb.test_pdb_with_inline_breakpoint[0]>(5)test_function()->None
+    -> x = 2
+    display x: 2  [old: 1]
+    (Pdb) undisplay
+    (Pdb) c
+    """
+
 def test_pdb_issue_20766():
     """Test for reference leaks when the SIGINT handler is set.
 
diff --git a/Misc/NEWS.d/next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst b/Misc/NEWS.d/next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst
new file mode 100644 (file)
index 0000000..4a65fb7
--- /dev/null
@@ -0,0 +1,4 @@
+Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now
+reuse the most recent ``Pdb`` instance that calls ``Pdb.set_trace()``,
+instead of creating a new one each time. As a result, all the instance specific
+data like ``display`` and ``commands`` are preserved across Hard-coded breakpoints.