Skip to content

Commit ebe52e7

Browse files
Make NopReceiver.ready() respond to close() method
The ready() method was not terminating when close() was called, causing it to hang indefinitely. This fix ensures ready() returns False when the receiver is closed. Signed-off-by: Elzbieta Kotulska <[email protected]>
1 parent 6f24b25 commit ebe52e7

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414

1515
## Bug Fixes
1616

17-
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
17+
- Fix `NopReceiver.ready()` to properly terminate when receiver is closed.

src/frequenz/channels/experimental/_nop_receiver.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class NopReceiver(Receiver[ReceiverMessageT_co]):
2020

2121
def __init__(self) -> None:
2222
"""Initialize this instance."""
23-
self._closed: bool = False
23+
self._ready_future: asyncio.Future[bool] = asyncio.Future()
2424

2525
@override
2626
async def ready(self) -> bool:
@@ -29,9 +29,9 @@ async def ready(self) -> bool:
2929
Returns:
3030
Whether the receiver is still active.
3131
"""
32-
if self._closed:
32+
if self._ready_future.done():
3333
return False
34-
await asyncio.Future()
34+
await self._ready_future
3535
return False
3636

3737
@override
@@ -47,11 +47,12 @@ def consume(self) -> ReceiverMessageT_co: # noqa: DOC503 (raised indirectly)
4747
ReceiverStoppedError: If the receiver stopped producing messages.
4848
ReceiverError: If there is some problem with the underlying receiver.
4949
"""
50-
if self._closed:
50+
if self._ready_future.done():
5151
raise ReceiverStoppedError(self)
5252
raise ReceiverError("`consume()` must be preceded by a call to `ready()`", self)
5353

5454
@override
5555
def close(self) -> None:
5656
"""Stop the receiver."""
57-
self._closed = True
57+
if not self._ready_future.done():
58+
self._ready_future.set_result(False)

tests/experimental/test_nop_receiver.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,28 @@ async def test_consuming_raises() -> None:
3838
receiver.close()
3939
with pytest.raises(ReceiverStoppedError):
4040
receiver.consume()
41+
42+
43+
async def test_close_method_effect_on_ready() -> None:
44+
"""Test `ready()` terminates when the receiver is closed.
45+
46+
When the receiver is closed, `ready()` should return False.
47+
This test verifies that:
48+
1. `ready()` returns False immediately after `close()` is called
49+
2. `ready()` and `close()` can be called multiple times
50+
"""
51+
receiver = NopReceiver[int]()
52+
53+
# Create a task that waits for the receiver to be ready.
54+
task = asyncio.create_task(receiver.ready())
55+
56+
# Wait for the task to start.
57+
await asyncio.sleep(0.1)
58+
59+
# Close the receiver and wait for the task to complete.
60+
receiver.close()
61+
assert await asyncio.wait_for(task, timeout=0.1) is False
62+
63+
# Second call to `close()` or `ready()` should not raise an error.
64+
receiver.close()
65+
assert await asyncio.wait_for(receiver.ready(), timeout=0.1) is False

0 commit comments

Comments
 (0)