Background
I'm developing a piece of software for a hardware system that will, in practice, have several expected and frequent points of failure - my software runs on a laptop computer and is responsible for sending commands to a microcontroller, which forwards those commands to some test devices, then passes their responses back to the computer. The overall system can't be restructured at this point, and the test conditions mean that all the connections are exposed to unexpected interruptions. The code I inherited handles such a disconnect by silently crashing - obviously not a good response. I've refactored the code to recognize when and where a disconnect has occurred and raise an exception in response, and now I'm looking at how to handle the exception.
How to set a default handler
The exception is raised in a class called firmware, which handles communication to and from the microcontroller. I need to handle it in my user interface class, which has the error-raising firmware object as one of it's data members. The error can arise from any operation that interacts with the microcontroller, so the naïve approach to handling it would be to wrap each function call in a try block:
try:
self.firmware.doSomethingErrorProne()
except (serial.SerialException, BadConnectionError) as e:
# salvage the state as much as possible and alert the user
However, this would result in a lot of code duplication, as almost all my functions handle this failure in the same way. What I'm looking for is a way to have a 'default' handler that will manage the exception unless it is preempted by a case-specific handler. My first thought was to use nested try blocks, but there's no origination point shared by all the functions - they're called by clicking buttons in a pyqt window.
Possible Solution?
One idea I'm looking at as a solution is to create a function that takes as its argument a function in the firmware class, then calls that function inside a try block with the appropriate handling. Then, in each of my dangerous functions, I can call that indirect function instead of directly calling the function I want from firmware. I think this would work, but it doesn't feel like a very elegant/clean solution, and one of the main goals here is to make the code cleaner. Is there a best practice for what I'm trying to achieve? Alternatively, would it be better to back up and use a different approach entirely for this situation?
Handling the error directly in firmware isn't currently possible because the response requires access to the userInterface data members, and firmware doesn't have a reference to the userInterface object. I could change that, but doing so would create a case of two classes where each instance of either has a unique instance of the other tied to it, and members of both were directly accessed by the other - which again doesn't really feel like good design.