Skip to content

Commit 9f2433c

Browse files
nficanoclaude
andcommitted
refactor phase 7: asyncio.timeout, ASYNC109 noqa for public API
Replace 3x asyncio.wait_for(future, timeout=t) with async with asyncio.timeout(t): await future in runtime.job (request_human_input/choice timeout paths) and client.request (3.11+ structured-cancellation primitive). Fan-out is already TaskGroup throughout the runtime; no gather sites. ASYNC109 ("async function with a timeout parameter") fires on client.request because the timeout is part of the public API. Add a per-line noqa with reason on the parameter. For test helpers that use 'timeout' as a polling-budget wall-clock value rather than as an asyncio.timeout argument, add ASYNC109 to the tests/** per-file ignore. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent eb9ea94 commit 9f2433c

3 files changed

Lines changed: 15 additions & 5 deletions

File tree

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ ignore = [
8484
[tool.ruff.lint.per-file-ignores]
8585
# Tools (`async def f(ctx, args)`) and inbound handlers (`async def h(env)`)
8686
# have protocol-mandated signatures; unused-arg lints are noise here.
87-
"tests/**" = ["B011", "ARG001", "ARG002"]
87+
# ASYNC109 in tests fires on test-helper polling deadlines that use a
88+
# `timeout=` parameter as a wall-clock budget rather than asyncio.timeout().
89+
"tests/**" = ["B011", "ARG001", "ARG002", "ASYNC109"]
8890
"examples/**" = ["ARG001", "ARG002"]
8991

9092
[tool.ruff.lint.pydocstyle]

src/arcp/client/client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ async def send(self, envelope: Envelope) -> None:
129129
raise RuntimeError("client is not open")
130130
await self.transport.send(envelope.to_wire())
131131

132-
async def request(self, envelope: Envelope, *, timeout: float | None = None) -> Envelope:
132+
async def request(
133+
self,
134+
envelope: Envelope,
135+
*,
136+
timeout: float | None = None, # noqa: ASYNC109 — public API; pass-through to asyncio.timeout below.
137+
) -> Envelope:
133138
"""Send a command and await the envelope whose ``correlation_id`` matches.
134139
135140
``correlation_id`` matching is keyed on the outbound envelope's ``id``.
@@ -142,7 +147,8 @@ async def request(self, envelope: Envelope, *, timeout: float | None = None) ->
142147
self._waiters[envelope.id] = future
143148
try:
144149
await self.transport.send(envelope.to_wire())
145-
return await asyncio.wait_for(future, timeout=timeout)
150+
async with asyncio.timeout(timeout):
151+
return await future
146152
finally:
147153
self._waiters.pop(envelope.id, None)
148154

src/arcp/runtime/job.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ async def request_human_input(
259259
await self.sink(envelope)
260260
timeout = max(0.0, _seconds_until(expires_at))
261261
try:
262-
response = await asyncio.wait_for(future, timeout=timeout)
262+
async with asyncio.timeout(timeout):
263+
response = await future
263264
except TimeoutError as exc:
264265
self.pending.cancel(request_id)
265266
if default is not None:
@@ -327,7 +328,8 @@ async def request_human_choice(
327328
await self.sink(envelope)
328329
timeout = max(0.0, _seconds_until(expires_at))
329330
try:
330-
response = await asyncio.wait_for(future, timeout=timeout)
331+
async with asyncio.timeout(timeout):
332+
response = await future
331333
except TimeoutError as exc:
332334
self.pending.cancel(request_id)
333335
self.job.state = prior_state

0 commit comments

Comments
 (0)