Skip to content

Commit a6c142b

Browse files
author
Hermes PR Bot
committed
fix: handle non-file stdio in tests by catching UnsupportedOperation
When sys.stdin/sys.stdout are monkeypatched with StringIO/BytesIO (e.g. in unit tests), fileno() raises io.UnsupportedOperation. Fall back to the original non-duping behavior in that case. Fixes CI test failures on all Python versions.
1 parent cacd81b commit a6c142b

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

src/mcp/server/stdio.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,24 @@ async def stdio_server(stdin: anyio.AsyncFile[str] | None = None, stdout: anyio.
4141
# re-wrap the underlying binary stream to ensure UTF-8.
4242
# We duplicate the file descriptors to avoid closing the original stdio
4343
# when the transport is closed (fixes issue #1933).
44+
# When stdin/stdout are not real OS files (e.g. StringIO/BytesIO in tests),
45+
# fileno() raises io.UnsupportedOperation; fall back to the original path.
4446
if not stdin:
45-
stdin_fd = os.dup(sys.stdin.fileno())
46-
stdin_bin = os.fdopen(stdin_fd, "rb", closefd=True)
47-
stdin = anyio.wrap_file(TextIOWrapper(stdin_bin, encoding="utf-8", errors="replace"))
47+
try:
48+
stdin_fd = os.dup(sys.stdin.fileno())
49+
except io.UnsupportedOperation:
50+
stdin = anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8", errors="replace"))
51+
else:
52+
stdin_bin = os.fdopen(stdin_fd, "rb", closefd=True)
53+
stdin = anyio.wrap_file(TextIOWrapper(stdin_bin, encoding="utf-8", errors="replace"))
4854
if not stdout:
49-
stdout_fd = os.dup(sys.stdout.fileno())
50-
stdout_bin = os.fdopen(stdout_fd, "wb", closefd=True)
51-
stdout = anyio.wrap_file(TextIOWrapper(stdout_bin, encoding="utf-8"))
55+
try:
56+
stdout_fd = os.dup(sys.stdout.fileno())
57+
except io.UnsupportedOperation:
58+
stdout = anyio.wrap_file(TextIOWrapper(sys.stdout.buffer, encoding="utf-8"))
59+
else:
60+
stdout_bin = os.fdopen(stdout_fd, "wb", closefd=True)
61+
stdout = anyio.wrap_file(TextIOWrapper(stdout_bin, encoding="utf-8"))
5262

5363
read_stream_writer, read_stream = create_context_streams[SessionMessage | Exception](0)
5464
write_stream, write_stream_reader = create_context_streams[SessionMessage](0)

0 commit comments

Comments
 (0)