Exceptions and Results

Evan Matizza
Software Development

In working with Python and Django, Exceptions can come out of nowhere. Borrowing a simple application domain from django-zen-queries, assume the following basic pizza shop model:

Django has some wonderful utilities for getting an item and returning a view like get_model_or_404. This works great within a large or function based view, but we usually use services for some of our more complex logic, and going from Model -> HttpResponse is a little too quick, where our services are typically nested inside our views and are fallible.

So, we need an intermediate type. Our services are fallible, so they can be one of two things: Ok(val), with some wrapped return value, or an Error, abbreviated Err(err), with one of many possible Errors specific to our service and it's underlying details.

In the case of our friend .get on a model item's default manager, .objects this looks like:

But, this can raise an exception! Specifically, there are two exceptions, provided we passed a correct filter (Q('name=="Cheese"')). The two exceptions that can be raised by this code path are ObjectDoesNotExist, and MultipleObjectsReturned.

Enter exception handling:

Enter another dependent model lookup, and you'll start writing the following:

Pretty verbose, and definitely explicit! I wonder if there is a type that could wrap this logic safely, and give the user a more pattern matching type approach as seen from our colleagues in the rust and functional world...

Enter result. It's got an API modeled after Rust's Result Type, and has a covariant and contravariant type constructor (thats happy path and sad path respectively for the mere mortals). When combined with case match from newer python versions, and structural unpacking (which you can read about here), handling errors can remain explicit. It can also be much less verbose, and more fluent and compact. Assume a function for our result type:

We can then use it as follows:

Hopefully that inspires you to take a look at result, and exploring how types can make your dev experience a bit more managed and explicit.

Continue reading