From 634725c0b60b2d8abaaa113852980be520d4e6da Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Sat, 6 Jul 2024 16:21:55 -0700 Subject: [PATCH 1/8] Make inline breakpoints use the most recent pdb instance --- Lib/bdb.py | 1 + Lib/pdb.py | 11 ++++++++++- Lib/test/test_pdb.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Lib/bdb.py b/Lib/bdb.py index aa621053cfb4bc..d7543017940d4f 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -369,6 +369,7 @@ def set_trace(self, frame=None): 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() diff --git a/Lib/pdb.py b/Lib/pdb.py index 85a3aa2e37996f..d7e9feebe43f5b 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -306,6 +306,12 @@ class Pdb(bdb.Bdb, cmd.Cmd): _file_mtime_table = {} + _last_pdb_instance = None + + def __new__(self, *args, **kwargs): + Pdb._last_pdb_instance = super().__new__(self) + return Pdb._last_pdb_instance + def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True): bdb.Bdb.__init__(self, skip=skip) @@ -2350,7 +2356,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) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5c7445574f5d75..625125f2efd240 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2448,6 +2448,49 @@ def test_pdb_show_attribute_and_item(): (Pdb) c """ +# doctest will override 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(): + """Inline 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() + > (3)test_function() + -> import pdb; pdb.Pdb().set_trace() + (Pdb) display x + display x: 1 + (Pdb) n + > (4)test_function() + -> original_pdb_settrace() + (Pdb) n + > (4)test_function() + -> original_pdb_settrace() + (Pdb) n + > (5)test_function() + -> x = 2 + (Pdb) n + --Return-- + > (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. @@ -3347,6 +3390,8 @@ def test_header(self): with ExitStack() as resources: resources.enter_context(patch('sys.stdout', stdout)) resources.enter_context(patch.object(pdb.Pdb, 'set_trace')) + # Make sure pdb.set_trace() is using a patched pdb instance + _ = pdb.Pdb() pdb.set_trace(header=header) self.assertEqual(stdout.getvalue(), header + '\n') From c15480cff57bb96d136e47bc527916f41cf617a7 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 23:39:40 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst 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 index 00000000000000..58fb337ba4e568 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst @@ -0,0 +1 @@ +Inline breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) will use the most recent ``Pdb`` instance, instead of creating a new one. From a72864d6c2decf233d71f8286b0b708cb71ddb06 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Tue, 9 Jul 2024 18:51:57 -0700 Subject: [PATCH 3/8] Fix docs/comments --- Lib/test/test_pdb.py | 4 ++-- .../Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 625125f2efd240..31cf1fcbc634a1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -2448,12 +2448,12 @@ def test_pdb_show_attribute_and_item(): (Pdb) c """ -# doctest will override pdb.set_trace during the test, so we need to backup +# 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(): - """Inline breakpoint() calls should invoke the same debugger instance + """Hard-coded breakpoint() calls should invoke the same debugger instance >>> def test_function(): ... x = 1 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 index 58fb337ba4e568..a228bb9b480ff2 100644 --- 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 @@ -1 +1 @@ -Inline breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) will use the most recent ``Pdb`` instance, instead of creating a new one. +Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now reuse the most recent ``Pdb`` instance, instead of creating a new one each time From 9233cc792df9e6965366aa5fab40dcca64ca7a41 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 10 Jul 2024 12:42:55 -0700 Subject: [PATCH 4/8] Set _last_pdb_instance in Pdb.set_trace() --- Lib/pdb.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/pdb.py b/Lib/pdb.py index d7e9feebe43f5b..7ff973149b167b 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -308,10 +308,6 @@ class Pdb(bdb.Bdb, cmd.Cmd): _last_pdb_instance = None - def __new__(self, *args, **kwargs): - Pdb._last_pdb_instance = super().__new__(self) - return Pdb._last_pdb_instance - def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, nosigint=False, readrc=True): bdb.Bdb.__init__(self, skip=skip) @@ -365,6 +361,12 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, 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 From 6d438a3b05811855641843b37ff6cef8a3501728 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 10 Jul 2024 12:45:39 -0700 Subject: [PATCH 5/8] Update news --- .../Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 index a228bb9b480ff2..723f1b03322726 100644 --- 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 @@ -1 +1,4 @@ -Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now reuse the most recent ``Pdb`` instance, instead of creating a new one each time +Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now +reuse the most recent ``Pdb`` instance that calls :func:`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. From fa7574bbe8d38fdeaa0a019726fa819ea29943e7 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 10 Jul 2024 12:49:46 -0700 Subject: [PATCH 6/8] Does not need the explicit instantiation anymore --- Lib/test/test_pdb.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 31cf1fcbc634a1..343e15a4edc14c 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -3390,8 +3390,6 @@ def test_header(self): with ExitStack() as resources: resources.enter_context(patch('sys.stdout', stdout)) resources.enter_context(patch.object(pdb.Pdb, 'set_trace')) - # Make sure pdb.set_trace() is using a patched pdb instance - _ = pdb.Pdb() pdb.set_trace(header=header) self.assertEqual(stdout.getvalue(), header + '\n') From b15c3b2f29e28ac3b4262b6f1cce00bde58c30db Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 10 Jul 2024 12:56:39 -0700 Subject: [PATCH 7/8] Fix lint --- .../next/Library/2024-07-06-23-39-38.gh-issue-121450.vGqb3c.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 723f1b03322726..4a65fb737f025b 100644 --- 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 @@ -1,4 +1,4 @@ Hard-coded breakpoints (:func:`breakpoint` and :func:`pdb.set_trace()`) now -reuse the most recent ``Pdb`` instance that calls :func:`Pdb.set_trace()`, +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. From 73b0e9fa2eee1e191cd7e5d61043860e78d223a3 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Wed, 10 Jul 2024 16:46:26 -0700 Subject: [PATCH 8/8] Add what's new entry --- Doc/whatsnew/3.14.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d02c10ec9cf3f3..da9b45cd8e58b3 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -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 --------