11import sys
22
3+ from typing import BinaryIO
34from typing import Optional
45
56import anyio
1516
1617class StdioClient (StreamIO ):
1718
18- def __init__ (self ):
19+ def __init__ (self , stdout_buffer : BinaryIO ):
20+ self ._stdout_buffer = stdout_buffer
1921 self .stream = StapledByteStream (
20- FileWriteStream (sys . stdout . buffer ),
22+ FileWriteStream (stdout_buffer ),
2123 FileReadStream (sys .stdin .buffer ),
2224 )
2325
@@ -26,7 +28,7 @@ async def read(self, size: int) -> bytes:
2628
2729 async def write (self , data : bytes ):
2830 await self .stream .send (data )
29- await anyio .to_thread .run_sync (sys . stdout . buffer .flush )
31+ await anyio .to_thread .run_sync (self . _stdout_buffer .flush )
3032
3133 async def close (self ):
3234 await self .stream .aclose ()
@@ -35,15 +37,20 @@ async def close(self):
3537class StdioServer (StreamServer ):
3638
3739 _client : StdioClient
40+ _stdout_buffer : BinaryIO
3841
3942 def __init__ (self , handler : Optional [Handler ] = None ):
4043 super ().__init__ (handler )
41-
44+ # Capture the real stdout buffer before redirecting sys.stdout.
45+ # Any print() in user code after this point goes to stderr,
46+ # keeping the binary Content-Length-framed protocol on fd 1 clean.
47+ self ._stdout_buffer = sys .stdout .buffer
48+ sys .stdout = sys .stderr
4249
4350 def wrap_io (self , client : StreamIO ) -> StreamIO :
4451 return PrefixStreamIO (client )
4552
4653 async def run (self ):
4754 print ("StdioServer started" , file = sys .stderr , flush = True )
48- self ._client = StdioClient ()
49- await self ._handle_client (self ._client )
55+ self ._client = StdioClient (self . _stdout_buffer )
56+ await self ._handle_client (self ._client )
0 commit comments