From 4712def2b1740b30d528018dd14405493c2055e8 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 24 Jan 2023 12:42:06 +0000 Subject: [PATCH] bglib: convert uncaught exceptions to events This does not solve the underlying problem of an unhandled exception in the connection handler thread, but means that the outer app layer actually receives an event, giving them a chance of taking action. We only have a single string field, but the traceback is still important, so include it anyway, as a string, it's better than nothing. Signed-off-by: Karl Palsson --- bgapi/bglib.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bgapi/bglib.py b/bgapi/bglib.py index 08be378..e276293 100644 --- a/bgapi/bglib.py +++ b/bgapi/bglib.py @@ -22,6 +22,7 @@ from array import array import threading +import traceback try: import queue except ImportError: @@ -76,6 +77,8 @@ def __init__(self, connection, event_handler, *apis): threading.Thread.__init__(self) self.daemon = True # Daemon thread self.stop_flag = threading.Event() + threading.excepthook = self._handle_thread_exceptions + self._apis, = apis self.conn = connection self.event_handler = event_handler @@ -86,6 +89,14 @@ def __init__(self, connection, event_handler, *apis): self.response_queue = queue.Queue(maxsize=1) self.waiting_response = threading.Event() + def _handle_thread_exceptions(self, ee): + # This is called on the thread that excepted + # It's just nicer than wrapping everything in a try: block + # Create a synthetic event for the outer world to discover that the thread has gone. + # We can't keep the traceback, as system.error only has definitions for code+string + val = f"{ee.exc_type}:{ee.exc_value} {traceback.format_tb(ee.exc_traceback)}" + self.event_handler(BGEvent(self._apis["system"].events["error"], (99, val))) + def send_command(self, bgcmd, response_timeout): try: # Send command