Skip to content

gh-132536: Do not disable PY_THROW event in bdb #132537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions Lib/bdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def start_trace(self, tracefunc):
E = sys.monitoring.events
all_events = 0
for event, cb_name in self.EVENT_CALLBACK_MAP.items():
callback = getattr(self, f'{cb_name}_callback')
callback = self.callback_wrapper(getattr(self, f'{cb_name}_callback'), event)
sys.monitoring.register_callback(self._tool_id, event, callback)
if event != E.INSTRUCTION:
all_events |= event
Expand All @@ -82,19 +82,22 @@ def restart_events(self):
if sys.monitoring.get_tool(self._tool_id) == self._name:
sys.monitoring.restart_events()

def callback_wrapper(func):
def callback_wrapper(self, func, event):
import functools

@functools.wraps(func)
def wrapper(self, *args):
def wrapper(*args):
if self._tracing_thread != threading.current_thread():
return
try:
frame = sys._getframe().f_back
ret = func(self, frame, *args)
ret = func(frame, *args)
if self._enabled and frame.f_trace:
self.update_local_events()
if self._disable_current_event:
if (
self._disable_current_event
and event not in (E.PY_THROW, E.PY_UNWIND, E.RAISE)
):
return sys.monitoring.DISABLE
else:
return ret
Expand All @@ -107,30 +110,25 @@ def wrapper(self, *args):

return wrapper

@callback_wrapper
def call_callback(self, frame, code, *args):
local_tracefunc = self._tracefunc(frame, 'call', None)
if local_tracefunc is not None:
frame.f_trace = local_tracefunc
if self._enabled:
sys.monitoring.set_local_events(self._tool_id, code, self.LOCAL_EVENTS)

@callback_wrapper
def return_callback(self, frame, code, offset, retval):
if frame.f_trace:
frame.f_trace(frame, 'return', retval)

@callback_wrapper
def unwind_callback(self, frame, code, *args):
if frame.f_trace:
frame.f_trace(frame, 'return', None)

@callback_wrapper
def line_callback(self, frame, code, *args):
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)

@callback_wrapper
def jump_callback(self, frame, code, inst_offset, dest_offset):
if dest_offset > inst_offset:
return sys.monitoring.DISABLE
Expand All @@ -141,7 +139,6 @@ def jump_callback(self, frame, code, inst_offset, dest_offset):
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)

@callback_wrapper
def exception_callback(self, frame, code, offset, exc):
if frame.f_trace:
if exc.__traceback__ and hasattr(exc.__traceback__, 'tb_frame'):
Expand All @@ -152,7 +149,6 @@ def exception_callback(self, frame, code, offset, exc):
tb = tb.tb_next
frame.f_trace(frame, 'exception', (type(exc), exc, exc.__traceback__))

@callback_wrapper
def opcode_callback(self, frame, code, offset):
if frame.f_trace and frame.f_trace_opcodes:
frame.f_trace(frame, 'opcode', None)
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2581,6 +2581,41 @@ def test_pdb_next_command_subiterator():
(Pdb) continue
"""

def test_pdb_breakpoint_with_throw():
"""GH-132536: PY_THROW event should not be turned off

>>> reset_Breakpoint()

>>> def gen():
... yield 0

>>> def test_function():
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
... g = gen()
... try:
... g.throw(TypeError)
... except TypeError:
... pass

>>> with PdbTestInput([
... 'b 7',
... 'continue',
... 'clear 1',
... 'continue',
... ]):
... test_function()
> <doctest test.test_pdb.test_pdb_breakpoint_with_throw[2]>(2)test_function()
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
(Pdb) b 7
Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_with_throw[2]>:7
(Pdb) continue
> <doctest test.test_pdb.test_pdb_breakpoint_with_throw[2]>(7)test_function()
-> pass
(Pdb) clear 1
Deleted breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_with_throw[2]>:7
(Pdb) continue
"""

def test_pdb_multiline_statement():
"""Test for multiline statement

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Do not disable :monitoring-event:`PY_THROW` event in :mod:`bdb` because it can't be disabled.
Loading