Exception Handling In Python - Designing Modules | HashedIn

Exception Handling In Python - Designing Modules Part - 3

Technology - 08 Dec 2016
Harish Thyagarajan

This is the third post in a multi-part series where we will design a reusable python module. In an earlier part, we created the interface of our module. This post will discuss in detail, the implementation of a robust SMS module.

Here’s a quick recap of the requirements:

You are the developer on Blipkart, an e-commerce website. You have been asked to build a reusable SMS module that other developers will use to send SMS’es. Watertel is a telecom company that provides a REST API for sending SMS’es, and your module needs to integrate with Watertel. Your module will be used by other modules such as logistics.py and orders.py.

To start sending SMS’es, we need to first login to Watertel, store the accessToken and the time when the accessToken expires.

You don’t expect or want client developers to think about login, hence we make the login method private (start with underscore).

Now we need to decide who calls the _login method. One easy (and wrong) choice, is calling the _login method from our constructor. Constructors must not do real work, but only aid in initialization. If we make network calls in the constructor, it makes the object difficult to test, and difficult to reuse.

So _login cannot be called by clients, and cannot be called in the constructor. Which leaves us with only one choice – call _login from the send_sms method. And while we are at it, we will add another wrapper to directly get us the access_token without worrying about expiry. This also gives the necessary modularity to our code.

At this point we can start writing code to send the SMS. We will assume a make_request function that returns the parsed JSON object and the http status code.

Error Handling in Python – Status Codes

What should we do with the status_code? One option is to return the status_code and let our clients handle it. And that is a bad idea.

We don’t want our clients to know how we are sending the SMS. This is important – if the clients know how you send the sms, you cannot change how your module works in the future. When we return the HTTP status code, we are telling them indirectly how our module works. This is called a leaky abstraction, and you want to avoid it as much as possible (though you can’t eliminate it completely).

So to give our code the much-needed abstraction, we will not return the status code, but we still want our clients to do some error handling. Since our clients are calling a python method, they expect to receive errors the pythonic way – using Exception Handling in Python.

When it comes to error handling, you need to be clear whose problem it is. It’s either the client developer’s fault, or your module’s fault. A problem with your module’s dependency (i.e. Watertel server down) is also your module’s fault – because the client developer doesn’t know Watertel even exists.

We will create an exception class for each possible error in the module –

  • BadInputError – a base error for incorrect inputs
  • InvalidPhoneNumberError – when the phone number is malformed or wrong
  • InvalidMessageError – when the message is longer than 140 characters
  • SmsException – when it’s our modules fault and we cannot send the sms. This tells our clients that calling send_sms again is safe and may work.

On implementing python exception handling, here is how our code looks like:

Error Handling in Python – Exceptions

Apart from error handling of status codes, there’s one more thing that is missing – handling any exceptions that are raised by _make_request. Assuming you are using the wonderful requests library, this is the list of exceptions that can be thrown.

You can categorize these exceptions into two categories – safe to retry v/s not safe to retry. If it is safe to retry, wrap the exception in a SMSException and raise it. Otherwise, just let the exception propagate.

In our case, ConnectTimeout is safe to retry. ReadTimeout indicates the request made it to the server, but the server did not respond timely. In such cases, you can’t be sure if the SMS was sent or not. If it is critical the SMS be sent, you should retry. If your end users would be annoyed receiving multiple SMSes, then do not retry.

You can handle these exceptions inside the _make_request method.

This should have given you a gist of how exception handling in python can help build a cleaner and more robust code.

That covers most of the implementation of our module. We won’t get into the code that actually makes the HTTP request – you can use the requests library for that.

Summary

  1. Don’t leak inner workings of your module to your clients, otherwise you can never refactor or change your implementation.
  2. Raise appropriate Errors to inform your clients what went wrong.

In the next post, we will see how our module holds up to new requirements.

Free tag for commerce

E-book on Digital Business Transformation