Learn to properly define your own exceptions when built-in exceptions in python are insufficient

pythons built-in exceptions are great but not good enough…

Imran Ali
Level Up Coding

--

Photo by Romson Preechawit on Unsplash

New programmers are often confused about the topic of exception handling and look for best practices in adopting exception handling into their existing code but without proper knowledge of how exception handling works under the hood it can cause their programs to behave unexpectedly.

In this article I will present a simple use case where using the built-in python exception is insufficient and results in an obscure error message that I will work through and attempt to improve on starting with using the built-in exception initially and then gradually defining our own custom exception to demonstrate a somewhat realistic thinking process as a programmer works through the program

Let’s begin with the example

This program tries to calculate the area of a triangle. If you recall from your secondary school geometry class, a triangle is invalid if the sum of any of its two sides is less than the third side.

Here’s what an implementation of this function looks like

If you run this in your terminal twice first with a valid triangle of sides (3,4,5) and then an invalid triangle of sides (3,4,10)

you get a ValueError built-in exception with a message payload “math domain error”. If I am the user of this program I have no idea of what this error means and how to correct my input

Let's modify the program and try to raise a more specific exception here which carries more useful information in its payload to help our program users. A good start would be to define a TriangleError exception that inherits from the Exception class.

That looks a very simple definition and would indeed suffice as it inherits implementations of __init__ , __str__ and __repr__ from the base class Exception.

if we just want a distinct exception class which has some basic facilities and can be handled and raised separately from other exception types then this definition would suffice and is fully functional

Now lets modify our triangle_area function to raise this exception

running the program with same inputs again we get

That output is much better as the user can clearly identify that the error TriangleError has to do something with the triangle geometry and we are creating and invalid triangle. But notice the payload information doesn’t provide any information about the sides so lets keep working on it

Here we override all the key methods from the base class __init__, __str__ and __repr__. super().__init__(text) stores the payload message into base class Exception. Inside __str__ and __repr__ we use that payload message that we stored in the base class by retrieving it through self.args[0]. sides is defined as a property so our instance variables can be accessed like a public attribute. More advantages to using property here

Lets modify our triangle_area function to use this improve error class

All we had to do is add sides argument to the TriangleError class. Let’s run the example now

Look at that error report. It looks fantastic…doesn’t it? Not only is the user getting the right error type but also the payload information makes it clear that the sides input is wrong. But not only that we can now handle this exception in our program and inspect its payload to get the culprit side lengths. Also, note how we are accessing the sides attribute. This is made possible by defining sides as a property

Conclusion

Built-in exceptions are sometimes less helpful than a custom one. You can make your program communicate domain-relevant information which can be very useful in a large library or program

--

--