fix: Prevent retry_on_errors from retrying asyncio.CancelledError

The retry_on_errors decorator in mcp_session_manager.py now catches asyncio.CancelledError and re-raises it immediately, ensuring that cancellation requests are not suppressed or retried

Close #4009

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 852468382
This commit is contained in:
George Weale
2026-01-05 14:58:26 -08:00
committed by Copybara-Service
parent 6f2c70fc33
commit 30d3411d60
2 changed files with 105 additions and 15 deletions
@@ -391,7 +391,8 @@ class TestMCPSessionManager:
assert "Close error 1" in error_output
def test_retry_on_errors_decorator():
@pytest.mark.asyncio
async def test_retry_on_errors_decorator():
"""Test the retry_on_errors decorator."""
call_count = 0
@@ -401,23 +402,77 @@ def test_retry_on_errors_decorator():
nonlocal call_count
call_count += 1
if call_count == 1:
import anyio
raise anyio.ClosedResourceError("Resource closed")
raise ConnectionError("Resource closed")
return "success"
@pytest.mark.asyncio
async def test_retry():
mock_self = Mock()
result = await mock_function(mock_self)
assert result == "success"
assert call_count == 2 # First call fails, second succeeds
@pytest.mark.asyncio
async def test_retry_on_errors_decorator_does_not_retry_cancelled_error():
"""Test the retry_on_errors decorator does not retry cancellation."""
call_count = 0
@retry_on_errors
async def mock_function(self):
nonlocal call_count
call_count = 0
call_count += 1
raise asyncio.CancelledError()
mock_self = Mock()
result = await mock_function(mock_self)
mock_self = Mock()
with pytest.raises(asyncio.CancelledError):
await mock_function(mock_self)
assert result == "success"
assert call_count == 2 # First call fails, second succeeds
assert call_count == 1
# Run the test
import asyncio
asyncio.run(test_retry())
@pytest.mark.asyncio
async def test_retry_on_errors_decorator_does_not_retry_when_task_is_cancelling():
"""Test the retry_on_errors decorator does not retry when cancelling."""
call_count = 0
@retry_on_errors
async def mock_function(self):
nonlocal call_count
call_count += 1
raise ConnectionError("Resource closed")
class _MockTask:
def cancelling(self):
return 1
mock_self = Mock()
with patch.object(asyncio, "current_task", return_value=_MockTask()):
with pytest.raises(ConnectionError):
await mock_function(mock_self)
assert call_count == 1
@pytest.mark.asyncio
async def test_retry_on_errors_decorator_does_not_retry_exception_from_cancel():
"""Test the retry_on_errors decorator does not retry exceptions on cancel."""
call_count = 0
@retry_on_errors
async def mock_function(self):
nonlocal call_count
call_count += 1
try:
raise asyncio.CancelledError()
except asyncio.CancelledError:
raise ConnectionError("Resource closed")
mock_self = Mock()
with pytest.raises(ConnectionError):
await mock_function(mock_self)
assert call_count == 1