Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are some elegant ways to abstract out repetitive exception handling in python?

When handling exceptions in python, I find myself repeating code quite often. The basic pattern is something of the form:

try:
  action_here()
except CommonException1:
  Action_always_taken_for_CommonException1()
except CommonException2:
  Action_always_taken_for_CommonException2()
except Exception:
  Default_action_always_taken()

What I would like to do is to some how abstract this repetitive code out to a function or class. I know one way to do it is to call an exception handling function with the exception object, such as:

try:
  action_here()
except Exception as e:
  handle_exception(e)

Then in this function determine the exception based on class.

def handle_exception(e):
  if type(e) == type(CommonException1()):
    Action_always_taken_for_CommonException1()
  elif type(e) == type(CommonException2()):
    Action_always_taken_for_CommonException2())
  else:
    Default_action_always_taken()

This, however, feels clunky and inelegant. So my question is, what are some other alternatives to handling repetitive exception handling?

like image 738
cledoux Avatar asked Mar 21 '11 06:03

cledoux


2 Answers

This situation is one of the main use cases for context managers and the with statement:

from __future__ import with_statement # Needed in 2.5, but not in 2.6 or later

from contextlib import contextmanager

@contextmanager
def handle_exceptions():
    try:
        yield # Body of the with statement effectively runs here
    except CommonException1:
        Action_always_taken_for_CommonException1()
    except CommonException2:
        Action_always_taken_for_CommonException2()
    except Exception:
        Default_action_always_taken()

# Used as follows
with handle_exceptions():
    action_here()
like image 64
ncoghlan Avatar answered Oct 11 '22 14:10

ncoghlan


If you dislike the repeated if / elseif blocks, you could put your handles in a dict, keyed by type:

handlers = { type(CommonException1()) : Action_always_taken_forCommonException1,
             type(CommonException2()) : Action_always_taken_forCommonException2 }

def handle_exception(te):
  if te in handlers:
    handlers[te]()
  else:
    Default_action()

Which you could then run with:

try:
  action_here()
except Exception as e:
  handle_exception(type(e))

In addition: If you find yourself writing these try blocks frequently, then you could write your own context manager (see here). At the action_here() side, your code would then look like this:

with my_error_handling_context():
  action_here1()
  action_here2()

In this case, the handle_exception code would essentially be your context manager's __exit__ method (which will always get passed any exceptions raised during the with block).

like image 22
phooji Avatar answered Oct 11 '22 15:10

phooji