Skip to content

Commit 7f8347f

Browse files
xndcnbizfsc
andauthored
network: Avoid notifier re-creating in connect function (#621)
* network: Avoid notifier re-creating in connect function * test_network: Add regression tests for notifier lifecycle Add tests to verify that: - connect() does not recreate an existing notifier - disconnect() releases the notifier - disconnect() releases the notifier even when check() raises Co-authored-by: Frieder Schüler <frieder.schueler@bizerba.com>
1 parent a4e61d7 commit 7f8347f

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

canopen/network.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def connect(self, *args, **kwargs) -> Network:
107107
if self.bus is None:
108108
self.bus = can.Bus(*args, **kwargs)
109109
logger.info("Connected to '%s'", self.bus.channel_info)
110-
self.notifier = can.Notifier(self.bus, self.listeners, self.NOTIFIER_CYCLE)
110+
if self.notifier is None:
111+
self.notifier = can.Notifier(self.bus, self.listeners, self.NOTIFIER_CYCLE)
111112
return self
112113

113114
def disconnect(self) -> None:
@@ -123,7 +124,11 @@ def disconnect(self) -> None:
123124
if self.bus is not None:
124125
self.bus.shutdown()
125126
self.bus = None
126-
self.check()
127+
try:
128+
self.check()
129+
finally:
130+
# Release notifier after check
131+
self.notifier = None
127132

128133
def __enter__(self):
129134
return self

test/test_network.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ def test_network_check(self):
6969
def cleanup():
7070
# We must clear the fake exception installed below, since
7171
# .disconnect() implicitly calls .check() during test tear down.
72-
self.network.notifier.exception = None
72+
if self.network.notifier is not None:
73+
self.network.notifier.exception = None
7374
self.network.disconnect()
7475

7576
self.addCleanup(cleanup)
@@ -285,6 +286,34 @@ def wait_for_periodicity():
285286
if msg is not None:
286287
self.assertIsNone(bus.recv(PERIOD))
287288

289+
def test_network_connect_does_not_recreate_notifier(self):
290+
self.network.connect(interface="virtual")
291+
self.addCleanup(self.network.disconnect)
292+
notifier1 = self.network.notifier
293+
self.assertIsNotNone(notifier1)
294+
# Calling connect() again should reuse the existing notifier
295+
self.network.connect(interface="virtual")
296+
self.assertIs(self.network.notifier, notifier1)
297+
298+
def test_network_disconnect_releases_notifier(self):
299+
self.network.connect(interface="virtual")
300+
self.assertIsNotNone(self.network.notifier)
301+
self.network.disconnect()
302+
self.assertIsNone(self.network.notifier)
303+
304+
def test_network_disconnect_releases_notifier_on_exception(self):
305+
self.network.connect(interface="virtual")
306+
307+
class Custom(Exception):
308+
pass
309+
310+
self.network.notifier.exception = Custom("fake")
311+
with self.assertRaises(Custom):
312+
with self.assertLogs(level=logging.ERROR):
313+
self.network.disconnect()
314+
# Notifier must be released even when check() raises
315+
self.assertIsNone(self.network.notifier)
316+
288317

289318
class TestScanner(unittest.TestCase):
290319
TIMEOUT = 0.1

0 commit comments

Comments
 (0)