I have never really been a fan of how exceptions look like in code. Mostly because of the nesting it causes , it is already giving me flashbacks of callback hell. And this has made me use them less in my code, or even not think about using them which comes back to bite me from time to time.
However, since I have been making an embedded system using Micropython I have come to find a big appreciation for them. Having come from the backend world, I actually never experienced my whole app crashing because of an exception occurring. Over there, the server spins a new thread of execution whenever a request is received and the thread crashes on its own and does not crash the whole program.
Contrast this with embedded system development, if an exception occurs your whole application crashes. And this is unexceptable if you want your system to be resilient and available 24/7. And here is where our hero the try and except block comes along. Saving you from the danger of you code crashing and you being in the dark about it.
What I decided to do was to wrap the setup and loop part of my code in exceptions as shown below
import blah_blah_blah
try:
setup()
except Exception as e:
do_something()
while True:
try:
app()
except Exception as e:
handle_loop_exception()
Of course you can be more specific in your exceptions and structure. I think this is what they call failing gracefully and with style if you wish.
Some of the things that resulted in exceptions that I had not forseen include:
Index out of range exception
Sometimes you expect a list to have N elements but it has fewer and you expect a certain index to have the value you want but now it is nowhere to be found.
data = some_string.split(',')
answer = data[4]
Like in the above , we expect that if we split the string using a comma, it will at least 5 elements but that is not always the case and you need to deal with such an issue at runtime
KeyError Exception
In this case you are accessing a non existant key from a dictionary. using the syntax
a = my_dict['alien']
instead of this syntax, I would suggest using
a = my_dict.get('alien')
In which case you would get None if the key is not in the dictionary.
But what if you have a nested dictionary. Being lazy I have left that as an exercise to the reader
Memory Error
I am guilty of assuming sometimes that I am still writing a program that will run on a server or pc. I have encountered this error when I have tried to read and manipulate some long log files. And you might lose data from the file that you are trying to write to if this exception occurs
with open('file.txt', 'w') as f:
f.write('here I am')
In the above example if there is not enough memory to allocate for the file content and exception would occur and you would even lose your file content
Threads and Locks
I'll finish of with where I really got myself into trouble when I was not using exceptions, that is, when I had acquired a lock and an exception occured before I released it causing a deadlock. Without logs I wonder if I would have ever gotten myself out of that problem. But the lesson was clear, just wrap the operations in between the acquiring and release of a lock in a try except block as below
lock.acquire()
try:
# Critical section
print("Shared resource accessed")
finally:
lock.release()
NB: You can deal with exceptions where they occur and not just 'globally' as I have shown, this is just to catch any exceptions that you might have forgotten about.
Thank your for your time and hope this helps somewhere.
Top comments (0)