Skip to content

Commit 8412806

Browse files
committed
fix: preserve existing sys.settrace when entering/exiting DebugContext
- Store previous trace function in _previous_trace before setting new trace - Restore previous trace on exit instead of unconditionally clearing to None - Skip sys.settrace calls entirely when context is disabled - Add tests for disabled context behavior and previous trace restoration
1 parent 1c78bd8 commit 8412806

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

src/helper/debugger.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ def __init__(self, name, enabled):
1212
self.name = name
1313
self.enabled = enabled
1414
self.logging = logging.getLogger(__name__)
15+
self._previous_trace = None
1516

1617
def __enter__(self):
1718
"""Set trace calls on entering debugger."""
1819
self.logging.debug("Entering debug context for %s", self.name)
20+
if not self.enabled:
21+
return self
22+
self._previous_trace = sys.gettrace()
1923
sys.settrace(self.trace_calls)
2024
return self
2125

2226
def __exit__(self, exc_type, exc_val, exc_tb):
2327
"""Remove trace on exiting debugger."""
24-
sys.settrace(None)
28+
if self.enabled:
29+
sys.settrace(self._previous_trace)
2530
return False
2631

2732
def trace_calls(self, frame, event, _arg):

tests/unit/test_debugger.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ def test_returns_self(self):
4141
returned = ctx.__enter__()
4242
assert returned is ctx
4343

44+
def test_disabled_context_does_not_set_sys_trace(self):
45+
ctx = DebugContext("my_func", False)
46+
with patch.object(sys, "settrace") as mock_settrace:
47+
returned = ctx.__enter__()
48+
mock_settrace.assert_not_called()
49+
assert returned is ctx
50+
4451

4552
class TestDebugContextExit:
4653
"""Tests for DebugContext.__exit__."""
@@ -51,6 +58,20 @@ def test_clears_sys_trace(self):
5158
ctx.__exit__(None, None, None)
5259
mock_settrace.assert_called_once_with(None)
5360

61+
def test_restores_previous_trace(self):
62+
previous_trace = object()
63+
ctx = DebugContext("my_func", True)
64+
ctx._previous_trace = previous_trace
65+
with patch.object(sys, "settrace") as mock_settrace:
66+
ctx.__exit__(None, None, None)
67+
mock_settrace.assert_called_once_with(previous_trace)
68+
69+
def test_disabled_context_exit_does_not_clear_trace(self):
70+
ctx = DebugContext("my_func", False)
71+
with patch.object(sys, "settrace") as mock_settrace:
72+
ctx.__exit__(None, None, None)
73+
mock_settrace.assert_not_called()
74+
5475
def test_returns_false(self):
5576
ctx = DebugContext("my_func", True)
5677
with patch.object(sys, "settrace"):

0 commit comments

Comments
 (0)