Skip to content

Commit 0d92192

Browse files
authored
Shorten stdio test comments (#3329)
1 parent b2025ab commit 0d92192

6 files changed

Lines changed: 62 additions & 262 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ assigned to their author are closed automatically.
4545

4646
## Code Quality
4747

48+
- Keep comments brief. Explain only non-obvious reasons or constraints; do not
49+
narrate the code or restate names, types, or assertions.
4850
- Type hints required for all code
4951
- Public APIs must have docstrings. When a public API raises exceptions a
5052
caller would reasonably catch, document them in a `Raises:` section. Don't

tests/interaction/transports/_stdio_server.py

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
"""A real low-level Server over the stdio transport, for the suite's one subprocess test.
2-
3-
Runnable as `python -m tests.interaction.transports._stdio_server` from the repo root; the test
4-
launches it that way via `stdio_client`. Kept separate from the test module so the server lives in
5-
its own importable file (subprocess coverage applies) while the test file follows the suite's
6-
test-only-functions convention.
7-
"""
1+
"""Low-level stdio server for the interaction suite's subprocess test."""
82

93
import sys
104
import warnings
@@ -63,26 +57,13 @@ async def set_logging_level(ctx: ServerRequestContext, params: SetLevelRequestPa
6357
async def main() -> None:
6458
async with stdio_server() as (read_stream, write_stream):
6559
await server.run(read_stream, write_stream, server.create_initialization_options())
66-
# Flush this process's coverage data before the clean-exit line below. Without this, the
67-
# data is only written by coverage's atexit hook during interpreter teardown -- and on a
68-
# slow Windows runner that can overrun the transport's termination grace, so the kill
69-
# silently destroys the data file and the 100% gate trips on this module's subprocess-only
70-
# lines. Saving here puts the write before the line the test synchronizes on: once the
71-
# parent has seen "clean exit", the data is durably on disk and the escalation is harmless.
72-
# Nothing measured may execute after the save (it would be unrecordable by construction),
73-
# hence the excluded lines below. The branch is pragma'd because under coverage the
74-
# instance always exists, and without coverage nothing is measured anyway.
60+
# Save subprocess coverage before the marker so forced teardown cannot lose it.
7561
cov = getattr(coverage.process_startup, "coverage", None)
7662
if cov is not None: # pragma: no branch
77-
# stop() is load-bearing twice over: it ends tracing, making itself the last
78-
# recordable line, and it leaves nothing new for coverage's atexit re-save to flush --
79-
# so a kill landing during interpreter teardown cannot corrupt the file save() wrote
80-
# (coverage opens it with sqlite journaling off; a torn rewrite would not roll back).
63+
# Leave nothing for coverage's atexit hook to rewrite if teardown is interrupted.
8164
cov.stop()
8265
cov.save() # pragma: lax no cover - untraced: stop() above already ended measurement
83-
# Reached only when the run loop exits because stdin closed; if the process were terminated
84-
# the test's stderr capture would not see this line. lax no cover: runs after the coverage
85-
# save by design, so it can never appear covered.
66+
# The test uses this marker to distinguish clean exit from termination.
8667
print("stdio-echo: clean exit", file=sys.stderr, flush=True) # pragma: lax no cover
8768

8869

tests/interaction/transports/test_stdio.py

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
"""The stdio transport: one subprocess end-to-end test and one in-process framing test.
1+
"""Stdio subprocess round-trip and in-process framing tests.
22
3-
The subprocess test proves the client-server round trip over the transport's real process
4-
boundary; its server lives in `_stdio_server.py` and is launched via `python -m` so subprocess
5-
coverage measurement applies. The framing test drives `stdio_server` over injected in-process
6-
streams instead.
7-
8-
stdio is deliberately not a leg of the `connect`-fixture matrix: a subprocess per test would be
9-
slow, and the matrix already proves transport-agnosticism in-process. Process-lifecycle edge
10-
cases (terminate/kill escalation, parse errors) stay in `tests/client/test_stdio.py`.
3+
Lifecycle edge cases remain in `tests/client/test_stdio.py`.
114
"""
125

136
import io
@@ -50,20 +43,8 @@
5043
async def test_tool_call_and_notification_round_trip_over_a_stdio_subprocess(
5144
monkeypatch: pytest.MonkeyPatch,
5245
) -> None:
53-
"""A stdio-subprocess Client round-trips a tool call, a notification, and a clean exit.
54-
55-
The Client initializes, calls a tool with arguments, and receives the server's log
56-
notification before the call returns; the server exits when the transport closes its
57-
stdin.
58-
"""
59-
# After stdin closes, the child must unwind, flush its subprocess coverage data, and write
60-
# the clean-exit line before escalation (the server saves coverage *before* printing, so a
61-
# post-print kill can no longer silently lose the data file -- see _stdio_server.main). The
62-
# production 2s default is too tight for the unwind+save tail on loaded Windows runners
63-
# (measured in-situ p99 of the whole test is ~7s); a kill before the print fails the stderr
64-
# assertion below loudly rather than tripping the coverage gate. The 20s grace covers even a
65-
# badly starved runner (a >10s stall has been seen once in CI) and costs nothing when the
66-
# child exits promptly. Not under test.
46+
"""A stdio client round-trips a tool call and notification before clean exit."""
47+
# Allow slow Windows runners to flush subprocess coverage before escalation.
6748
monkeypatch.setattr(stdio, "PROCESS_TERMINATION_TIMEOUT", 20.0)
6849

6950
received: list[LoggingMessageNotificationParams] = []
@@ -77,10 +58,7 @@ async def collect(params: LoggingMessageNotificationParams) -> None:
7758
command=sys.executable,
7859
args=["-m", _stdio_server.__name__],
7960
cwd=str(_REPO_ROOT),
80-
# stdio_client filters the inherited environment, dropping the variables
81-
# coverage.py's subprocess support uses; pass them through so the server module is
82-
# measured. PYTHONWARNINGS: the child recompiles anyio (pytest's pyc tag differs),
83-
# and on 3.14 anyio's return-in-finally SyntaxWarning would land on the snapshot stderr.
61+
# Preserve subprocess coverage and suppress anyio's `SyntaxWarning` on Python 3.14.
8462
env={key: value for key, value in os.environ.items() if key.startswith("COVERAGE_")}
8563
| {"PYTHONWARNINGS": "ignore::SyntaxWarning"},
8664
),
@@ -98,26 +76,18 @@ async def collect(params: LoggingMessageNotificationParams) -> None:
9876
captured_stderr = errlog.read()
9977

10078
assert result == snapshot(CallToolResult(content=[TextContent(text="across\nprocesses")]))
101-
# stdio carries one ordered server-to-client stream, so the same notification-before-response
102-
# guarantee holds here as for the in-memory transport.
79+
# Stdio preserves notification-before-response ordering.
10380
assert received == snapshot(
10481
[LoggingMessageNotificationParams(level="info", logger="echo", data="echoing across\nprocesses")]
10582
)
106-
# The server writes this line only after its run loop returns on stdin close: seeing it proves
107-
# a self-exit, not the terminate escalation. The capture itself proves stderr passthrough.
83+
# The marker distinguishes clean exit from termination.
10884
assert captured_stderr == snapshot("stdio-echo: clean exit\n")
10985

11086

11187
@requirement("transport:stdio:stream-purity")
11288
@requirement("transport:stdio:no-embedded-newlines")
11389
async def test_stdio_server_writes_one_jsonrpc_message_per_line() -> None:
114-
"""Every `stdio_server` write is one valid JSON-RPC message on its own line.
115-
116-
Each line is newline-terminated with payload newlines JSON-escaped. This proves the
117-
transport's own framing over injected streams; the descriptor-level guard that keeps
118-
handler code off the wire is pinned by tests/server/test_stdio.py (see the narrowed
119-
divergence on `transport:stdio:stream-purity`).
120-
"""
90+
"""Each `stdio_server` write is one newline-terminated JSON-RPC message."""
12191
captured = io.StringIO()
12292
sent_line = json.dumps(initialize_body(request_id=1)) + "\n"
12393

@@ -148,7 +118,5 @@ async def test_stdio_server_writes_one_jsonrpc_message_per_line() -> None:
148118
assert len(lines) == 2
149119
messages = [jsonrpc_message_adapter.validate_json(line) for line in lines]
150120
assert [type(message).__name__ for message in messages] == snapshot(["JSONRPCResponse", "JSONRPCNotification"])
151-
# The newline inside the payload is JSON-escaped on the wire, not a literal newline that would
152-
# break the one-message-per-line framing.
153121
assert r"line\nbreak" in lines[0]
154122
assert r"two\nlines" in lines[1]

0 commit comments

Comments
 (0)