Skip to content

Commit 9160642

Browse files
Added a done() method to BackgroundService to check for completion
This is usefull if we have type: `asyncio.Task | Actor` and need to check if actor finished. Signed-off-by: Elzbieta Kotulska <[email protected]>
1 parent e1ce323 commit 9160642

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## New Features
1212

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
13+
* Added a `done()` method to `BackgroundService` to check for completion.
1414

1515
## Bug Fixes
1616

src/frequenz/sdk/actor/_background_service.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ def is_running(self) -> bool:
161161
"""
162162
return any(not task.done() for task in self._tasks)
163163

164+
def done(self) -> bool:
165+
"""Return whether this background service is done.
166+
167+
A service is considered done when all tasks
168+
are finished or raised exception or were cancelled.
169+
170+
Returns:
171+
Whether this background service is done.
172+
"""
173+
return not self.is_running
174+
164175
def cancel(self, msg: str | None = None) -> None:
165176
"""Cancel all running tasks spawned by this background service.
166177

tests/actor/test_background_service.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ async def test_construction_defaults() -> None:
5050
assert fake_service.name == str(id(fake_service))
5151
assert fake_service.tasks == set()
5252
assert fake_service.is_running is False
53+
assert fake_service.done() is True
5354
assert str(fake_service) == f"FakeService[{fake_service.name}]"
5455
assert repr(fake_service) == f"FakeService(name={fake_service.name!r}, tasks=set())"
5556

@@ -60,49 +61,60 @@ async def test_construction_custom() -> None:
6061
assert fake_service.name == "test"
6162
assert fake_service.tasks == set()
6263
assert fake_service.is_running is False
64+
assert fake_service.done() is True
6365

6466

6567
async def test_start_await() -> None:
6668
"""Test a background service starts and can be awaited."""
6769
fake_service = FakeService(name="test")
6870
assert fake_service.name == "test"
6971
assert fake_service.is_running is False
72+
assert fake_service.done() is True
7073

7174
# Is a no-op if the service is not running
7275
await fake_service.stop()
7376
assert fake_service.is_running is False
77+
assert fake_service.done() is True
7478

7579
fake_service.start()
7680
assert fake_service.is_running is True
81+
assert fake_service.done() is False
7782

7883
# Should stop immediately
7984
async with asyncio.timeout(1.0):
8085
await fake_service
8186

8287
assert fake_service.is_running is False
88+
assert fake_service.done() is True
8389

8490

8591
async def test_start_stop() -> None:
8692
"""Test a background service starts and stops correctly."""
8793
fake_service = FakeService(name="test", sleep=2.0)
8894
assert fake_service.name == "test"
8995
assert fake_service.is_running is False
96+
assert fake_service.done() is True
9097

9198
# Is a no-op if the service is not running
9299
await fake_service.stop()
93100
assert fake_service.is_running is False
101+
assert fake_service.done() is True
94102

95103
fake_service.start()
96104
assert fake_service.is_running is True
105+
assert fake_service.done() is False
97106

98107
await asyncio.sleep(1.0)
99108
assert fake_service.is_running is True
109+
assert fake_service.done() is False
100110

101111
await fake_service.stop()
102112
assert fake_service.is_running is False
113+
assert fake_service.done() is True
103114

104115
await fake_service.stop()
105116
assert fake_service.is_running is False
117+
assert fake_service.done() is True
106118

107119

108120
@pytest.mark.parametrize("method", ["await", "wait", "stop"])
@@ -114,6 +126,7 @@ async def test_start_and_crash(
114126
fake_service = FakeService(name="test", exc=exc)
115127
assert fake_service.name == "test"
116128
assert fake_service.is_running is False
129+
assert fake_service.done() is True
117130

118131
fake_service.start()
119132
with pytest.raises(BaseExceptionGroup) as exc_info:
@@ -141,9 +154,12 @@ async def test_async_context_manager() -> None:
141154
"""Test a background service works as an async context manager."""
142155
async with FakeService(name="test", sleep=1.0) as fake_service:
143156
assert fake_service.is_running is True
157+
assert fake_service.done() is False
144158
# Is a no-op if the service is running
145159
fake_service.start()
146160
await asyncio.sleep(0)
147161
assert fake_service.is_running is True
162+
assert fake_service.done() is False
148163

149164
assert fake_service.is_running is False
165+
assert fake_service.done() is True

0 commit comments

Comments
 (0)