Skip to content

Commit 7a6d762

Browse files
committed
test: add coverage for stderr exception handlers and empty lines
1 parent c17e2cd commit 7a6d762

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

tests/client/test_stdio.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,3 +905,74 @@ async def test_stderr_reader_exception_handling():
905905
# Give it time to process stderr
906906
await anyio.sleep(0.3)
907907
# Should not crash, just log the error
908+
909+
910+
@pytest.mark.anyio
911+
async def test_stderr_reader_final_buffer_exception():
912+
"""Test stderr reader handles exception in final buffer flush."""
913+
# Write stderr without trailing newline to trigger final buffer path
914+
script_content = textwrap.dedent(
915+
"""
916+
import sys
917+
sys.stderr.write("no newline")
918+
sys.stderr.flush()
919+
# Keep running briefly
920+
import time
921+
time.sleep(0.1)
922+
"""
923+
)
924+
925+
server_params = StdioServerParameters(
926+
command=sys.executable,
927+
args=["-c", script_content],
928+
)
929+
930+
# Mock _print_stderr to raise an exception only on final buffer call
931+
call_count = 0
932+
933+
def mock_print_stderr_side_effect(line, errlog):
934+
nonlocal call_count
935+
call_count += 1
936+
if call_count > 1: # Raise on subsequent calls (final buffer)
937+
raise Exception("Print failed on final buffer")
938+
939+
with patch("mcp.client.stdio._print_stderr", side_effect=mock_print_stderr_side_effect):
940+
async with stdio_client(server_params) as (_, _):
941+
await anyio.sleep(0.3)
942+
# Should not crash, just log the error
943+
944+
945+
@pytest.mark.anyio
946+
async def test_stderr_with_empty_lines():
947+
"""Test that empty stderr lines are skipped."""
948+
script_content = textwrap.dedent(
949+
"""
950+
import sys
951+
print("line1", file=sys.stderr, flush=True)
952+
print("", file=sys.stderr, flush=True) # Empty line
953+
print(" ", file=sys.stderr, flush=True) # Whitespace only
954+
print("line2", file=sys.stderr, flush=True)
955+
# Keep running
956+
try:
957+
while True:
958+
line = sys.stdin.readline()
959+
if not line:
960+
break
961+
except:
962+
pass
963+
"""
964+
)
965+
966+
server_params = StdioServerParameters(
967+
command=sys.executable,
968+
args=["-c", script_content],
969+
)
970+
971+
stderr_capture = io.StringIO()
972+
async with stdio_client(server_params, errlog=stderr_capture) as (_, _):
973+
await anyio.sleep(0.3)
974+
975+
stderr_output = stderr_capture.getvalue()
976+
# Should have line1 and line2, but not empty lines
977+
assert "line1" in stderr_output
978+
assert "line2" in stderr_output

0 commit comments

Comments
 (0)