Skip to content

Commit b3e39ac

Browse files
committed
fix(tests): 3.14 phantom branch arcs, Windows Proactor transport leak
Two more cases where running in-process exposes issues subprocess hid: 1. Python 3.14 coverage: phantom ->exit arcs on nested async with CMs. The WITH_EXCEPT_START suppression-check branch (did __aexit__ suppress?) gets misattributed through the exception table to outer CM lines when the inner body yields. Previously the SSE security tests never ran on 3.14 (uvicorn thread crashed before server started) so this didn't surface. Add # pragma: no branch to the two specific CM lines coverage flags. 2. Windows 3.13 lowest-direct: Proactor socket transports from MCP client connections don't always close before GC when clients disconnect abruptly from the module-scoped server. The transport __del__ ResourceWarning is collected by pytest's unraisable hook during a later test. Older httpx (lowest-direct) has worse transport cleanup than the locked version. Subprocess-based tests hid this — resource warnings in a subprocess die with the subprocess. Add the same PytestUnraisableExceptionWarning filter that test_sse.py uses.
1 parent 56fcfe0 commit b3e39ac

File tree

3 files changed

+20
-4
lines changed

3 files changed

+20
-4
lines changed

tests/client/test_http_unicode.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,15 @@ async def unicode_session() -> AsyncGenerator[ClientSession, None]:
120120
async with app.router.lifespan_context(app):
121121
transport = httpx.ASGITransport(app=app)
122122
async with httpx.AsyncClient(transport=transport, follow_redirects=True) as http_client:
123-
async with streamable_http_client("http://testserver/mcp", http_client=http_client) as (rs, ws):
123+
async with streamable_http_client( # pragma: no branch
124+
"http://testserver/mcp", http_client=http_client
125+
) as (rs, ws):
126+
# ^ coverage.py on 3.11+ misses phantom ->exit arcs from nested
127+
# async with CMs when the innermost body yields (async generator
128+
# frame unwinds through __aexit__ chains). On 3.14 the
129+
# WITH_EXCEPT_START suppression-check branch is misattributed
130+
# through the exception table to the *outer* CM line too.
124131
async with ClientSession(rs, ws) as session: # pragma: no branch
125-
# ^ coverage.py misses the ->exit arc on 3.11+ when yield is
126-
# nested inside multiple async with blocks
127132
await session.initialize()
128133
yield session
129134

tests/server/test_sse_security.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ async def test_sse_security_disabled():
140140
settings = TransportSecuritySettings(enable_dns_rebinding_protection=False)
141141
with run_server_in_thread(make_app(settings), lifespan="off") as url:
142142
async with httpx.AsyncClient(timeout=5.0) as client:
143-
async with client.stream("GET", f"{url}/sse", headers={"Host": "evil.com"}) as response:
143+
async with client.stream( # pragma: no branch
144+
"GET", f"{url}/sse", headers={"Host": "evil.com"}
145+
) as response:
144146
# Should connect successfully even with invalid host
145147
assert response.status_code == 200
146148

tests/shared/test_streamable_http.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@
6363
)
6464
from tests.test_helpers import run_server_in_thread
6565

66+
# On Windows, the Proactor event loop's socket transports don't always close
67+
# cleanly before GC when MCP clients disconnect abruptly from the module-scoped
68+
# server (e.g. test_streamable_http_client_session_termination opens and drops
69+
# two clients). The transport __del__ emits ResourceWarning which pytest's
70+
# unraisable collector picks up during a LATER test. These are Windows asyncio
71+
# internals, not bugs in the client code under test. Previously hidden by
72+
# subprocess isolation — subprocess resource warnings die with the subprocess.
73+
pytestmark = pytest.mark.filterwarnings("ignore::pytest.PytestUnraisableExceptionWarning")
74+
6675
# Test constants
6776
SERVER_NAME = "test_streamable_http_server"
6877
TEST_SESSION_ID = "test-session-id-12345"

0 commit comments

Comments
 (0)