From 5e90d21a082218bdb5187ebd5f3d02d48ccfc1c0 Mon Sep 17 00:00:00 2001 From: Kirill Spitsyn Date: Thu, 16 Oct 2025 22:32:11 -0700 Subject: [PATCH] Make `PrinterCamera` client reconnectable. Before this fix calling `PrinterCamera.connect()` after a `.disconnect()` would raise a `RuntimeError: threads can only be started once`, as `PrinterCamera.__thread` was created only once in `PrinterCamera.__init__`, and it looks like the `Thread` objects can not be reused. --- bambulabs_api/camera_client.py | 10 ++++++---- tests/test_camera_client.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/test_camera_client.py diff --git a/bambulabs_api/camera_client.py b/bambulabs_api/camera_client.py index 0244bfc..c75f325 100644 --- a/bambulabs_api/camera_client.py +++ b/bambulabs_api/camera_client.py @@ -26,9 +26,7 @@ def __init__( self.__hostname = str(hostname) self.__port = port - self.__thread = Thread(target=self.retriever) - self.__thread.daemon = True - + self.__thread = None self.last_frame = None self.alive = False @@ -44,6 +42,8 @@ def start(self): """ if not self.alive: self.alive = True + self.__thread = Thread(target=self.retriever) + self.__thread.daemon = True self.__thread.start() return True else: @@ -54,7 +54,9 @@ def stop(self): Stop the camera client """ self.alive = False - self.__thread.join() + if self.__thread is not None: + self.__thread.join() + self.__thread = None def get_frame(self): if self.last_frame is None: diff --git a/tests/test_camera_client.py b/tests/test_camera_client.py new file mode 100644 index 0000000..782cdd8 --- /dev/null +++ b/tests/test_camera_client.py @@ -0,0 +1,17 @@ +from unittest.mock import patch + +from bambulabs_api.camera_client import PrinterCamera + + +def test_camera_is_reusable(): + camera = PrinterCamera('1.2.3.4', 'fake_code') + + # Mock the retriever method so it doesn't do any network I/O. + with patch.object(camera, 'retriever', return_value=None): + # First cycle + camera.start() + camera.stop() + + # Second cycle + camera.start() # This was failing before. + camera.stop()