Skip to content

Commit fd97336

Browse files
committed
fix: assign receive-loop attributes before spawning recv task
1 parent e9c6e9f commit fd97336

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/acp/connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ def __init__(
8686
self._closed = False
8787
self._disconnected = False
8888
self._sender = (sender_factory or self._default_sender_factory)(self._writer, self._tasks)
89+
self._observers: list[StreamObserver] = list(observers or [])
90+
self._receive_timeout = receive_timeout
8991
if listening:
9092
self._recv_task = self._tasks.create(
9193
self._receive_loop(),
@@ -103,8 +105,6 @@ def __init__(
103105
self._run_notification,
104106
)
105107
self._dispatcher.start()
106-
self._observers: list[StreamObserver] = list(observers or [])
107-
self._receive_timeout = receive_timeout
108108

109109
async def close(self) -> None:
110110
"""Stop the receive loop and cancel any in-flight handler tasks."""

tests/test_rpc.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,28 @@ async def test_spawn_agent_process_roundtrip(tmp_path):
676676
assert test_client.notifications
677677

678678
assert process.returncode is not None
679+
680+
681+
@pytest.mark.asyncio
682+
async def test_connection_init_under_eager_task_factory(server):
683+
# Regression: under asyncio.eager_task_factory the receive loop runs synchronously
684+
# up to its first await inside Connection.__init__, so every attribute it reads
685+
# (e.g. _receive_timeout) must be assigned before _tasks.create(_receive_loop()).
686+
loop = asyncio.get_running_loop()
687+
previous_factory = loop.get_task_factory()
688+
loop.set_task_factory(asyncio.eager_task_factory)
689+
try:
690+
conn = Connection(
691+
lambda method, params, is_notification: None,
692+
server.client_writer,
693+
server.client_reader,
694+
receive_timeout=0.5,
695+
)
696+
finally:
697+
loop.set_task_factory(previous_factory)
698+
699+
assert conn._receive_timeout == 0.5
700+
# Let the loop tick once so any deferred receive-task crash would land.
701+
await asyncio.sleep(0)
702+
assert conn._disconnected is False
703+
await conn.close()

0 commit comments

Comments
 (0)