Skip to content

Commit 2c09a78

Browse files
committed
docs(utils): enhance CRStripper docstrings and type hinting
- Fix Return value to reflect input length after character removal.
1 parent 816c089 commit 2c09a78

File tree

2 files changed

+68
-11
lines changed

2 files changed

+68
-11
lines changed

Server/src/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
if sys.platform == 'win32':
1515
import msvcrt
16-
# Set binary mode on stdin/stdout to prevent automatic translation at the FD level
16+
# Set binary mode on stdout to prevent automatic translation at the FD level
1717
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
1818

1919

Server/src/utils/cr_stripper.py

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,75 @@
1+
"""
2+
Provides a utility to strip carriage return characters from output streams.
3+
4+
This module implements the `CRStripper` class, which wraps a file-like object
5+
to filter out carriage return (\r) characters during write operations.
6+
7+
Usage of this wrapper is essential for Model Context Protocol (MCP) communication
8+
over stdio, as it ensures consistent line endings and safeguards against
9+
protocol errors, particularly in Windows environments.
10+
"""
11+
12+
from typing import Any, BinaryIO
113

2-
# This utility strips carriage return characters (\r) from bytes or strings
314
class CRStripper:
4-
def __init__(self, stream):
15+
"""
16+
A file-like wrapper that strips carriage return (\r) characters from data before writing.
17+
18+
This class intercepts write calls to the underlying stream and removes all
19+
instances of '\r', ensuring that output is clean and consistent across
20+
different platforms.
21+
"""
22+
def __init__(self, stream: BinaryIO) -> None:
23+
"""
24+
Initialize the stripper with an underlying stream.
25+
26+
Args:
27+
stream (BinaryIO): The underlying file-like object or buffer to wrap (e.g., sys.stdout.buffer).
28+
"""
529
self._stream = stream
630

7-
def write(self, data):
8-
if isinstance(data, bytes):
9-
return self._stream.write(data.replace(b'\r', b''))
10-
if isinstance(data, str):
11-
return self._stream.write(data.replace('\r', ''))
12-
return self._stream.write(data)
31+
def write(self, data: bytes | bytearray | str) -> int:
32+
"""
33+
Write data to the underlying stream after stripping all carriage return characters.
34+
35+
Args:
36+
data (bytes | bytearray | str): The data to be written.
37+
38+
Returns:
39+
int: The number of bytes or characters processed (matches input length if successful).
40+
"""
41+
if isinstance(data, (bytes, bytearray)):
42+
stripped = data.replace(b'\r', b'')
43+
written = self._stream.write(stripped)
44+
elif isinstance(data, str):
45+
stripped = data.replace('\r', '')
46+
written = self._stream.write(stripped)
47+
else:
48+
return self._stream.write(data)
49+
50+
# If the underlying stream wrote all the stripped data, we report
51+
# that we wrote all the ORIGINAL data.
52+
# This prevents callers (like TextIOWrapper) from seeing a "partial write"
53+
# mismatch when we intentionally removed characters.
54+
if written == len(stripped):
55+
return len(data)
56+
57+
return written
1358

14-
def flush(self):
59+
def flush(self) -> None:
60+
"""
61+
Flush the underlying stream.
62+
"""
1563
return self._stream.flush()
1664

17-
def __getattr__(self, name):
65+
def __getattr__(self, name: str) -> Any:
66+
"""
67+
Delegate any attribute or method access to the underlying stream.
68+
69+
Args:
70+
name (str): The name of the attribute to access.
71+
72+
Returns:
73+
Any: The attribute or method from the wrapped stream.
74+
"""
1875
return getattr(self._stream, name)

0 commit comments

Comments
 (0)