diff --git a/examples/debugging/restore_builtin_breakpoint.py b/examples/debugging/restore_builtin_breakpoint.py index 6e141df..8960507 100644 --- a/examples/debugging/restore_builtin_breakpoint.py +++ b/examples/debugging/restore_builtin_breakpoint.py @@ -6,19 +6,46 @@ import tractor async def main() -> None: - async with tractor.open_nursery(debug_mode=True) as an: - assert os.environ['PYTHONBREAKPOINT'] == 'tractor._debug._set_trace' + # intially unset, no entry. + orig_pybp_var: int = os.environ.get('PYTHONBREAKPOINT') + assert orig_pybp_var in {None, "0"} + + async with tractor.open_nursery( + debug_mode=True, + ) as an: + assert an + assert ( + (pybp_var := os.environ['PYTHONBREAKPOINT']) + == + 'tractor.devx._debug._sync_pause_from_builtin' + ) # TODO: an assert that verifies the hook has indeed been, hooked # XD - assert sys.breakpointhook is not tractor._debug._set_trace + assert ( + (pybp_hook := sys.breakpointhook) + is not tractor.devx._debug._set_trace + ) + print( + f'$PYTHONOBREAKPOINT: {pybp_var!r}\n' + f'`sys.breakpointhook`: {pybp_hook!r}\n' + ) breakpoint() + pass # first bp, tractor hook set. - # TODO: an assert that verifies the hook is unhooked.. + # XXX AFTER EXIT (of actor-runtime) verify the hook is unset.. + # + # YES, this is weird but it's how stdlib docs say to do it.. + # https://docs.python.org/3/library/sys.html#sys.breakpointhook + assert os.environ.get('PYTHONBREAKPOINT') is orig_pybp_var assert sys.breakpointhook + + # now ensure a regular builtin pause still works breakpoint() + pass # last bp, stdlib hook restored + if __name__ == '__main__': trio.run(main) diff --git a/tests/devx/test_debugger.py b/tests/devx/test_debugger.py index ce4f1ed..3eeda65 100644 --- a/tests/devx/test_debugger.py +++ b/tests/devx/test_debugger.py @@ -1231,6 +1231,53 @@ def test_shield_pause( child.expect(EOF) +def test_breakpoint_hook_restored( + spawn, +): + ''' + Ensures our actor runtime sets a custom `breakpoint()` hook + on open then restores the stdlib's default on close. + + The hook state validation is done via `assert`s inside the + invoked script with only `breakpoint()` (not `tractor.pause()`) + calls used. + + ''' + child = spawn('restore_builtin_breakpoint') + + child.expect(PROMPT) + assert_before( + child, + [ + _pause_msg, + "