Handle errors in capture thread

This commit is contained in:
Julian Stirling 2025-04-09 15:36:43 +01:00
parent 5dd23f6ef4
commit ff1184d453
2 changed files with 113 additions and 14 deletions

View file

@ -0,0 +1,77 @@
"""
This module contains some utility functions and classes
"""
from threading import Thread
class ErrorCapturingThread(Thread):
"""
This is a a subclass or Thread. It wraps the target function in a
try-except block.
Execution will stop with an unhandled exception, but the exception will not be raised
until the join method is called. When the join method is called, the exception is
called in the calling thread.
This allows for error handling to be performed by the calling thread at the time that
join is run.
"""
def __init__(self, group=None, target=None, args=None, kwargs=None, daemon=None):
"""
Initialise with the same arguments as Thread
"""
# As all inputs are keywords we need to set the default values for args and kwargs:
if args is None:
args = ()
if kwargs is None:
kwargs = {}
# Make and empy list for the error.
self._error_buffer = []
# Add the target function end error buffer to the thread to the start of
# the argument list so they are passed to _wrap_and_catch_errors()
args = (target, self._error_buffer, *args)
# Start the thread with _wrap_and_catch_errors() as the target
super().__init__(
group=group,
target=_wrap_and_catch_errors,
args=args,
kwargs=kwargs,
daemon=daemon,
)
def join(self, timeout=None):
"""
Join when the thread is complete. If the thread ended due to an unhandled exception,
the exception will be raised when this method is called.
"""
super().join(timeout)
# If there is an error in the error buffer clear the buffer and raise it
if self._error_buffer:
err = self._error_buffer[0]
self._error_buffer = []
raise err
def _wrap_and_catch_errors(target, error_buffer, *args, **kwargs):
"""
This function is designed only to be used by
ErrorCapturingThread
It will run a target function in a try-except block
any errors caught are added to an empty list that
should be supplied as a keyword argument
Arguments:
* target - The target function to call
* error_buffer - An empty list that is used for returning
the exception from the thread
*args: The arguments for the target function
**keargs: The Keyword arguments for the target function
"""
try:
target(*args, **kwargs)
except BaseException as e:
error_buffer.append(e)