From 966acfec0c43d774a13eb2ed968553455a163188 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 13 May 2024 05:38:21 -0700 Subject: [PATCH 1/3] [3.12] gh-58933: Make pdb return to caller frame correctly when f_trace is not set (GH-118979) (cherry picked from commit f526314194f7fd15931025f8a4439c1765666e42) Co-authored-by: Tian Gao --- Lib/bdb.py | 27 ++++++++++ Lib/test/test_pdb.py | 52 +++++++++++++++++++ ...4-05-12-21-38-42.gh-issue-58933.0kgU2l.rst | 1 + 3 files changed, 80 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-05-12-21-38-42.gh-issue-58933.0kgU2l.rst diff --git a/Lib/bdb.py b/Lib/bdb.py index 0f3eec653baaad..3ca577740be5f5 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -157,6 +157,11 @@ def dispatch_return(self, frame, arg): # The user issued a 'next' or 'until' command. if self.stopframe is frame and self.stoplineno != -1: self._set_stopinfo(None, None) + # The previous frame might not have f_trace set, unless we are + # issuing a command that does not expect to stop, we should set + # f_trace + if self.stoplineno != -1: + self._set_caller_tracefunc(frame) return self.trace_dispatch def dispatch_exception(self, frame, arg): @@ -285,6 +290,19 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): # stoplineno >= 0 means: stop at line >= the stoplineno # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno +<<<<<<< HEAD +======= + self._set_trace_opcodes(opcode) + + def _set_caller_tracefunc(self, current_frame): + # Issue #13183: pdb skips frames after hitting a breakpoint and running + # step commands. + # Restore the trace function in the caller (that may not have been set + # for performance reasons) when returning from the current frame. + caller_frame = current_frame.f_back + if caller_frame and not caller_frame.f_trace: + caller_frame.f_trace = self.trace_dispatch +>>>>>>> f526314194... gh-58933: Make pdb return to caller frame correctly when f_trace is not set (#118979) # Derived classes and clients can call the following methods # to affect the stepping state. @@ -299,6 +317,7 @@ def set_until(self, frame, lineno=None): def set_step(self): """Stop after one line of code.""" +<<<<<<< HEAD # Issue #13183: pdb skips frames after hitting a breakpoint and running # step commands. # Restore the trace function in the caller (that may not have been set @@ -309,6 +328,14 @@ def set_step(self): caller_frame.f_trace = self.trace_dispatch self._set_stopinfo(None, None) +======= + self._set_stopinfo(None, None) + + def set_stepinstr(self): + """Stop before the next instruction.""" + self._set_stopinfo(None, None, opcode=True) + +>>>>>>> f526314194... gh-58933: Make pdb return to caller frame correctly when f_trace is not set (#118979) def set_next(self, frame): """Stop on the next line in or below the given frame.""" self._set_stopinfo(frame, None) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 5bdc5f22d0b798..f5d4a9984ebfbb 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -904,6 +904,58 @@ def test_post_mortem(): """ +def test_pdb_return_to_different_file(): + """When pdb returns to a different file, it should not skip if f_trace is + not already set + + >>> import pprint + + >>> class A: + ... def __repr__(self): + ... return 'A' + + >>> def test_function(): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... pprint.pprint(A()) + + >>> reset_Breakpoint() + >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + ... 'b A.__repr__', + ... 'continue', + ... 'return', + ... 'next', + ... 'return', + ... 'return', + ... 'continue', + ... ]): + ... test_function() + > (2)test_function() + -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + (Pdb) b A.__repr__ + Breakpoint 1 at :3 + (Pdb) continue + > (3)__repr__() + -> return 'A' + (Pdb) return + --Return-- + > (3)__repr__()->'A' + -> return 'A' + (Pdb) next + > ...pprint.py..._safe_repr() + -> return rep,... + (Pdb) return + --Return-- + > ...pprint.py..._safe_repr()->('A'...) + -> return rep,... + (Pdb) return + --Return-- + > ...pprint.py...format()->('A'...) + -> return... + (Pdb) continue + A + """ + + def test_pdb_skip_modules(): """This illustrates the simple case of module skipping. diff --git a/Misc/NEWS.d/next/Library/2024-05-12-21-38-42.gh-issue-58933.0kgU2l.rst b/Misc/NEWS.d/next/Library/2024-05-12-21-38-42.gh-issue-58933.0kgU2l.rst new file mode 100644 index 00000000000000..fa70b954e1e9ee --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-12-21-38-42.gh-issue-58933.0kgU2l.rst @@ -0,0 +1 @@ +Make :mod:`pdb` return to caller frame correctly when ``f_trace`` of the caller frame is not set From 67003d45e8dd6ee90dd3489aaba1737fc5a60577 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 13 May 2024 10:15:45 -0700 Subject: [PATCH 2/3] Fix conflict --- Lib/bdb.py | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/Lib/bdb.py b/Lib/bdb.py index 3ca577740be5f5..564d6c5e5324ed 100644 --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -290,9 +290,6 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): # stoplineno >= 0 means: stop at line >= the stoplineno # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno -<<<<<<< HEAD -======= - self._set_trace_opcodes(opcode) def _set_caller_tracefunc(self, current_frame): # Issue #13183: pdb skips frames after hitting a breakpoint and running @@ -302,7 +299,6 @@ def _set_caller_tracefunc(self, current_frame): caller_frame = current_frame.f_back if caller_frame and not caller_frame.f_trace: caller_frame.f_trace = self.trace_dispatch ->>>>>>> f526314194... gh-58933: Make pdb return to caller frame correctly when f_trace is not set (#118979) # Derived classes and clients can call the following methods # to affect the stepping state. @@ -317,25 +313,8 @@ def set_until(self, frame, lineno=None): def set_step(self): """Stop after one line of code.""" -<<<<<<< HEAD - # Issue #13183: pdb skips frames after hitting a breakpoint and running - # step commands. - # Restore the trace function in the caller (that may not have been set - # for performance reasons) when returning from the current frame. - if self.frame_returning: - caller_frame = self.frame_returning.f_back - if caller_frame and not caller_frame.f_trace: - caller_frame.f_trace = self.trace_dispatch self._set_stopinfo(None, None) -======= - self._set_stopinfo(None, None) - - def set_stepinstr(self): - """Stop before the next instruction.""" - self._set_stopinfo(None, None, opcode=True) - ->>>>>>> f526314194... gh-58933: Make pdb return to caller frame correctly when f_trace is not set (#118979) def set_next(self, frame): """Stop on the next line in or below the given frame.""" self._set_stopinfo(frame, None) From 9d12f3ad861eb8845e7f5533368c80b891be8ed7 Mon Sep 17 00:00:00 2001 From: Tian Gao Date: Mon, 13 May 2024 11:03:08 -0700 Subject: [PATCH 3/3] Fix test --- Lib/test/test_pdb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index f5d4a9984ebfbb..24324a37804ca0 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -929,10 +929,10 @@ def test_pdb_return_to_different_file(): ... 'continue', ... ]): ... test_function() - > (2)test_function() - -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + > (3)test_function() + -> pprint.pprint(A()) (Pdb) b A.__repr__ - Breakpoint 1 at :3 + Breakpoint 1 at :2 (Pdb) continue > (3)__repr__() -> return 'A'