Skip to content

Commit d549d59

Browse files
Varun SharmaCopilot
andcommitted
fix: use pragma lax no cover for version-conditional imports, add regression tests
- Replace 'pragma: no cover' with 'pragma: lax no cover' (matches repo convention for lines that may/may not be covered per environment) - Add test_start_failure_is_unwrapped for start() error path - Add test_issue_2114_except_specific_error_type regression test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Reported-by: maxisbey <#2114>
1 parent dc63022 commit d549d59

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

src/mcp/shared/_task_group.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
import anyio
1919
from anyio.abc import TaskGroup
2020

21-
if sys.version_info < (3, 11): # pragma: no cover
22-
from exceptiongroup import BaseExceptionGroup
21+
if sys.version_info < (3, 11): # pragma: lax no cover
22+
from exceptiongroup import BaseExceptionGroup # pragma: lax no cover
2323

2424

2525
def collapse_exception_group(exc: BaseExceptionGroup) -> BaseException: # type: ignore[type-arg]

tests/shared/test_task_group.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
create_mcp_task_group,
1313
)
1414

15-
if sys.version_info < (3, 11): # pragma: no cover
16-
from exceptiongroup import BaseExceptionGroup, ExceptionGroup
15+
if sys.version_info < (3, 11): # pragma: lax no cover
16+
from exceptiongroup import BaseExceptionGroup, ExceptionGroup # pragma: lax no cover
1717

1818
# ---------------------------------------------------------------------------
1919
# collapse_exception_group unit tests
@@ -159,3 +159,48 @@ async def failing() -> None:
159159
tg.start_soon(failing)
160160

161161
assert isinstance(exc_info.value.__cause__, BaseExceptionGroup)
162+
163+
164+
@pytest.mark.anyio
165+
async def test_start_failure_is_unwrapped() -> None:
166+
"""An exception from start() is also unwrapped."""
167+
168+
async def fail_on_start(*, task_status: TaskStatus[str] = anyio.TASK_STATUS_IGNORED) -> None:
169+
raise ConnectionError("startup failed")
170+
171+
with pytest.raises(ConnectionError, match="startup failed"):
172+
async with create_mcp_task_group() as tg:
173+
await tg.start(fail_on_start)
174+
175+
176+
# ---------------------------------------------------------------------------
177+
# Regression test for https://github.com/modelcontextprotocol/python-sdk/issues/2114
178+
# ---------------------------------------------------------------------------
179+
180+
181+
@pytest.mark.anyio
182+
async def test_issue_2114_except_specific_error_type() -> None:
183+
"""Callers can catch specific exception types without ExceptionGroup wrapping.
184+
185+
Before the fix, anyio task groups always wrapped exceptions in
186+
ExceptionGroup, making ``except ConnectionError:`` impossible.
187+
"""
188+
caught: BaseException | None = None
189+
try:
190+
async with create_mcp_task_group() as tg:
191+
192+
async def background() -> None:
193+
await anyio.sleep(999)
194+
195+
async def connect() -> None:
196+
raise ConnectionError("connection refused")
197+
198+
tg.start_soon(background)
199+
tg.start_soon(connect)
200+
201+
except ConnectionError as exc:
202+
caught = exc
203+
204+
assert caught is not None
205+
assert str(caught) == "connection refused"
206+
assert isinstance(caught.__cause__, BaseExceptionGroup)

0 commit comments

Comments
 (0)