From 8f4363aad7b2c6272a94d029b4c1d45cc7617b39 Mon Sep 17 00:00:00 2001 From: xndcn Date: Tue, 9 Dec 2025 11:45:39 +0800 Subject: [PATCH 1/2] network: Avoid notifier re-creating in connect function --- canopen/network.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/canopen/network.py b/canopen/network.py index 6a8d95f6..4474c192 100644 --- a/canopen/network.py +++ b/canopen/network.py @@ -107,7 +107,8 @@ def connect(self, *args, **kwargs) -> Network: if self.bus is None: self.bus = can.Bus(*args, **kwargs) logger.info("Connected to '%s'", self.bus.channel_info) - self.notifier = can.Notifier(self.bus, self.listeners, self.NOTIFIER_CYCLE) + if self.notifier is None: + self.notifier = can.Notifier(self.bus, self.listeners, self.NOTIFIER_CYCLE) return self def disconnect(self) -> None: @@ -123,7 +124,11 @@ def disconnect(self) -> None: if self.bus is not None: self.bus.shutdown() self.bus = None - self.check() + try: + self.check() + finally: + # Release notifier after check + self.notifier = None def __enter__(self): return self From ddb4f2805c22e3eae3f8c4d94f10e5210b18a0a3 Mon Sep 17 00:00:00 2001 From: xndcn Date: Fri, 1 May 2026 22:53:13 +0800 Subject: [PATCH 2/2] test: Add regression tests for notifier lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- test/test_network.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/test_network.py b/test/test_network.py index cd65ea71..d488dc41 100644 --- a/test/test_network.py +++ b/test/test_network.py @@ -285,6 +285,34 @@ def wait_for_periodicity(): if msg is not None: self.assertIsNone(bus.recv(PERIOD)) + def test_network_connect_does_not_recreate_notifier(self): + self.network.connect(interface="virtual") + self.addCleanup(self.network.disconnect) + notifier1 = self.network.notifier + self.assertIsNotNone(notifier1) + # Calling connect() again should reuse the existing notifier + self.network.connect(interface="virtual") + self.assertIs(self.network.notifier, notifier1) + + def test_network_disconnect_releases_notifier(self): + self.network.connect(interface="virtual") + self.assertIsNotNone(self.network.notifier) + self.network.disconnect() + self.assertIsNone(self.network.notifier) + + def test_network_disconnect_releases_notifier_on_exception(self): + self.network.connect(interface="virtual") + + class Custom(Exception): + pass + + self.network.notifier.exception = Custom("fake") + with self.assertRaises(Custom): + with self.assertLogs(level=logging.ERROR): + self.network.disconnect() + # Notifier must be released even when check() raises + self.assertIsNone(self.network.notifier) + class TestScanner(unittest.TestCase): TIMEOUT = 0.1