Skip to content

Commit 3241a72

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 3241a72

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-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: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from __future__ import annotations
77

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

1112
from mypy.ipc import IPCBase
1213

@@ -40,19 +41,76 @@ def send(connection: IPCBase, data: Any) -> None:
4041
connection.write(json.dumps(data))
4142

4243

43-
class WriteToConn:
44+
class WriteToConn(TextIO):
4445
"""Helper class to write to a connection instead of standard output."""
4546

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

50105
def write(self, output: str) -> int:
51106
resp: dict[str, Any] = {}
52107
resp[self.output_key] = output
53108
send(self.server, resp)
54109
return len(output)
55110

111+
def writable(self) -> bool:
112+
return True
113+
56114
def writelines(self, lines: Iterable[str]) -> None:
57115
for s in lines:
58116
self.write(s)

0 commit comments

Comments
 (0)