mirror of https://github.com/python/cpython
gh-58933: Make pdb return to caller frame correctly when f_trace is not set (#118979)
This commit is contained in:
parent
d8a82cca12
commit
f526314194
12
Lib/bdb.py
12
Lib/bdb.py
|
@ -165,6 +165,11 @@ class Bdb:
|
|||
# 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):
|
||||
|
@ -320,13 +325,12 @@ class Bdb:
|
|||
self.stoplineno = stoplineno
|
||||
self._set_trace_opcodes(opcode)
|
||||
|
||||
def _set_caller_tracefunc(self):
|
||||
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.
|
||||
if self.frame_returning:
|
||||
caller_frame = self.frame_returning.f_back
|
||||
caller_frame = current_frame.f_back
|
||||
if caller_frame and not caller_frame.f_trace:
|
||||
caller_frame.f_trace = self.trace_dispatch
|
||||
|
||||
|
@ -343,12 +347,10 @@ class Bdb:
|
|||
|
||||
def set_step(self):
|
||||
"""Stop after one line of code."""
|
||||
self._set_caller_tracefunc()
|
||||
self._set_stopinfo(None, None)
|
||||
|
||||
def set_stepinstr(self):
|
||||
"""Stop before the next instruction."""
|
||||
self._set_caller_tracefunc()
|
||||
self._set_stopinfo(None, None, opcode=True)
|
||||
|
||||
def set_next(self, frame):
|
||||
|
|
|
@ -1453,6 +1453,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()
|
||||
> <doctest test.test_pdb.test_pdb_return_to_different_file[2]>(2)test_function()
|
||||
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
|
||||
(Pdb) b A.__repr__
|
||||
Breakpoint 1 at <doctest test.test_pdb.test_pdb_return_to_different_file[1]>:3
|
||||
(Pdb) continue
|
||||
> <doctest test.test_pdb.test_pdb_return_to_different_file[1]>(3)__repr__()
|
||||
-> return 'A'
|
||||
(Pdb) return
|
||||
--Return--
|
||||
> <doctest test.test_pdb.test_pdb_return_to_different_file[1]>(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.
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Make :mod:`pdb` return to caller frame correctly when ``f_trace`` of the caller frame is not set
|
Loading…
Reference in New Issue