Skip to content

Commit aee0c2a

Browse files
committed
Complete implementation of stdio stub
This class replaced stdout and stderr to capture output, but caused issues because it didn't implement the full API of sys.stdout and sys.stderr. By fully stubbing the TextIO API we prevent issues with other code which uses more of the API than we had previously accounted for. Fixes #16678
1 parent 86190a0 commit aee0c2a

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

mypy/dmypy_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ def serve(self) -> None:
221221
while True:
222222
with server:
223223
data = receive(server)
224-
sys.stdout = WriteToConn(server, "stdout") # type: ignore[assignment]
225-
sys.stderr = WriteToConn(server, "stderr") # type: ignore[assignment]
224+
sys.stdout = WriteToConn(server, "stdout", sys.stdout.isatty())
225+
sys.stderr = WriteToConn(server, "stderr", sys.stderr.isatty())
226226
resp: dict[str, Any] = {}
227227
if "command" not in data:
228228
resp = {"error": "No command found in request"}

mypy/dmypy_util.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from __future__ import annotations
77

88
import json
9-
from typing import Any, Final, Iterable
9+
import io
10+
from types import TracebackType
11+
from typing import Any, Final, Iterable, Iterator, TextIO
1012

1113
from mypy.ipc import IPCBase
1214

@@ -40,19 +42,76 @@ def send(connection: IPCBase, data: Any) -> None:
4042
connection.write(json.dumps(data))
4143

4244

43-
class WriteToConn:
45+
class WriteToConn(TextIO):
4446
"""Helper class to write to a connection instead of standard output."""
4547

46-
def __init__(self, server: IPCBase, output_key: str) -> None:
48+
def __init__(self, server: IPCBase, output_key: str, isatty: bool) -> None:
4749
self.server = server
4850
self.output_key = output_key
51+
self._isatty = isatty
52+
53+
def __enter__(self) -> TextIO:
54+
return self
55+
56+
def __exit__(
57+
self,
58+
t: type[BaseException] | None,
59+
value: BaseException | None,
60+
traceback: TracebackType | None,
61+
) -> None:
62+
pass
63+
64+
def __iter__(self) -> Iterator[str]:
65+
raise io.UnsupportedOperation
66+
67+
def __next__(self) -> str:
68+
raise io.UnsupportedOperation
69+
70+
def close(self) -> None:
71+
pass
72+
73+
def fileno(self) -> int:
74+
raise OSError
75+
76+
def flush(self) -> None:
77+
pass
78+
79+
def isatty(self) -> bool:
80+
return self._isatty
81+
82+
def read(self, n: int = 0) -> str:
83+
raise io.UnsupportedOperation
84+
85+
def readable(self) -> bool:
86+
return False
87+
88+
def readline(self, limit: int = 0) -> str:
89+
raise io.UnsupportedOperation
90+
91+
def readlines(self, hint: int = 0) -> list[str]:
92+
raise io.UnsupportedOperation
93+
94+
def seek(self, offset: int, whence: int = 0) -> int:
95+
raise io.UnsupportedOperation
96+
97+
def seekable(self) -> bool:
98+
return False
99+
100+
def tell(self) -> int:
101+
raise io.UnsupportedOperation
102+
103+
def truncate(self, size: int | None = 0) -> int:
104+
raise io.UnsupportedOperation
49105

50106
def write(self, output: str) -> int:
51107
resp: dict[str, Any] = {}
52108
resp[self.output_key] = output
53109
send(self.server, resp)
54110
return len(output)
55111

112+
def writable(self) -> bool:
113+
return True
114+
56115
def writelines(self, lines: Iterable[str]) -> None:
57116
for s in lines:
58117
self.write(s)

0 commit comments

Comments
 (0)