Skip to content

Commit 72e49bb

Browse files
committed
Use explicit exceptions when cancelling listeners
1 parent e3c9fcf commit 72e49bb

File tree

4 files changed

+24
-17
lines changed

4 files changed

+24
-17
lines changed

tests/api/test_listeners.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import zigpy_znp.types as t
77
import zigpy_znp.commands as c
88
from zigpy_znp.api import OneShotResponseListener, CallbackResponseListener
9+
from zigpy_znp.exceptions import ShuttingDown
910

1011

1112
async def test_resolve(event_loop, mocker):
@@ -34,24 +35,24 @@ async def test_resolve(event_loop, mocker):
3435
assert (await future) == match
3536

3637
# Cancelling a callback will have no effect
37-
assert not callback_listener.cancel()
38+
callback_listener.set_exception(RuntimeError())
3839

3940
# Cancelling a one-shot listener does not throw any errors
40-
assert one_shot_listener.cancel()
41-
assert one_shot_listener.cancel()
42-
assert one_shot_listener.cancel()
41+
one_shot_listener.set_exception(RuntimeError())
42+
one_shot_listener.set_exception(RuntimeError())
43+
one_shot_listener.set_exception(RuntimeError())
4344

4445

4546
async def test_cancel(event_loop):
4647
# Cancelling a one-shot listener prevents it from being fired
4748
future = event_loop.create_future()
4849
one_shot_listener = OneShotResponseListener([c.SYS.Ping.Rsp(partial=True)], future)
49-
one_shot_listener.cancel()
50+
one_shot_listener.set_exception(RuntimeError())
5051

5152
match = c.SYS.Ping.Rsp(Capabilities=t.MTCapabilities.SYS)
5253
assert not one_shot_listener.resolve(match)
5354

54-
with pytest.raises(asyncio.CancelledError):
55+
with pytest.raises(RuntimeError):
5556
await future
5657

5758

@@ -95,7 +96,7 @@ async def test_api_cancel_listeners(connected_znp, mocker):
9596
assert not future.done()
9697
znp.close()
9798

98-
with pytest.raises(asyncio.CancelledError):
99+
with pytest.raises(ShuttingDown):
99100
await future
100101

101102
# add_done_callback won't be executed immediately

zigpy_znp/api.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@
3232
CallbackResponseListener,
3333
)
3434
from zigpy_znp.frames import GeneralFrame
35-
from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse
35+
from zigpy_znp.exceptions import (
36+
ShuttingDown,
37+
CommandNotRecognized,
38+
InvalidCommandResponse,
39+
)
3640
from zigpy_znp.types.nvids import ExNvIds, OsalNvIds
3741

3842
if typing.TYPE_CHECKING:
@@ -774,7 +778,7 @@ def close(self) -> None:
774778

775779
for _header, listeners in self._listeners.items():
776780
for listener in listeners:
777-
listener.cancel()
781+
listener.set_exception(ShuttingDown())
778782

779783
self._listeners.clear()
780784
self.version = None

zigpy_znp/exceptions.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from zigpy.exceptions import DeliveryError
1+
from zigpy.exceptions import DeliveryError, ControllerException
22

33

44
class InvalidFrame(ValueError):
@@ -17,3 +17,7 @@ class InvalidCommandResponse(DeliveryError):
1717
def __init__(self, message, response):
1818
super().__init__(message)
1919
self.response = response
20+
21+
22+
class ShuttingDown(ControllerException):
23+
pass

zigpy_znp/utils.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def _resolve(self, response: t.CommandBase) -> bool:
8686

8787
raise NotImplementedError() # pragma: no cover
8888

89-
def cancel(self):
89+
def set_exception(self, exc: BaseException) -> None:
9090
"""
9191
Implement by subclasses to cancel the listener.
9292
@@ -118,11 +118,9 @@ def _resolve(self, response: t.CommandBase) -> bool:
118118
self.future.set_result(response)
119119
return True
120120

121-
def cancel(self):
121+
def set_exception(self, exc: BaseException) -> None:
122122
if not self.future.done():
123-
self.future.cancel()
124-
125-
return True
123+
self.future.set_exception(exc)
126124

127125

128126
@dataclasses.dataclass(frozen=True)
@@ -149,9 +147,9 @@ def _resolve(self, response: t.CommandBase) -> bool:
149147
# Callbacks are always resolved
150148
return True
151149

152-
def cancel(self):
150+
def set_exception(self, exc: BaseException) -> None:
153151
# You can't cancel a callback
154-
return False
152+
pass
155153

156154

157155
class CatchAllResponse:

0 commit comments

Comments
 (0)