Musings: Using decorators to reduce duplicate exception handling

J Kenneth King james at agentultra.com
Thu Feb 19 10:28:52 EST 2009


Cameron Simpson <cs at zip.com.au> writes:

> On 17Feb2009 15:12, J Kenneth King <james at agentultra.com> wrote:
> | I recently started a project called TracShell
> | (http://code.google.com/p/tracshell) where I make heavy use of the
> | xmlrpclib core module.
> | 
> | When the number of RPC calls was small, wrapping each call in try/except
> | was acceptable. [...]
> | To combat the duplication, my clever idea was to use a function
> | decorator to wrap any function that used xmlrpclib calls:
> | def catch_errors(fn):
> [...]
> | Maybe I could rename the decorator to something meaningful, but besides
> | that it works pretty well. Now any function I write in my class that
> | uses RPC calls can be wrapped by this decorator and have the exception
> | handling done in a uniform way for these particular exceptions.
>
> My Python fu is still weak, but I had a similar issue last year and
> wrote a context manager, used thus:
>
>   with NoExceptions(handler):
>       ... do stuff that may break ...
>
> The constructor takes a handleException argument (which may be None, but it
> has to be explicit) to tune which exceptions are caught; default is
> everything. This was for a daemon thread that did RPC calls, so it was
> actually fairly important to never die; normally you'd want to catch only
> specific exceptions.
>
> Code:
>
>   class NoExceptions(object):
>       ''' A context manager to catch _all_ exceptions and log them.
>           Arguably this should be a bare try...except but that's syntacticly
>           noisy and separates the catch from the top.
>       '''
>
>       def __init__(self, handleException):
>           ''' Initialise the NoExceptions context manager.
>               The handleException is a callable which
>               expects (exc_type, exc_value, traceback)
>               and returns True or False for the __exit__
>               method of the manager.
>               If handleException is None, the __exit__ method
>               always returns True, suppressing any exception.
>           '''
>           self.__handler = handleException
>
>       def __enter__(self):
>           pass
>
>       def __exit__(self, exc_type, exc_value, traceback):
>           if self.__handler is not None:
>               return self.__handler(exc_type, exc_value, traceback)
>           if exc_type is not None:
>               print >>sys.stderr, "ignore %s" % (exc_type,)
>           return True
>
> It doesn't actually solve your duplication issue and is a bit of a niche
> application, but it's a lot shorter than an inline try/except and to my eye
> reads a little better, and it keeps the exception handling up front at the
> calling end where it is visible.
>
> Cheers,

Ah, that's pretty interesting. I've found a couple other decorator-style
recipes in the cookbooks online (http://code.activestate.com/recipes/408937/).

I think you see my point: every single RPC call needs to be caught just
in case because they are so volatile -- yet you're always looking for
the exact same exception everywhere... too noisy to have inline
try/except. I wonder if there are other use cases where Python
developers have found the need to abstract away repetitive exception
handling.

I think this is the first I've seen the newer Python context managers
being used (though it sneakily looks a lot like what Lisp calls,
"unwind-protect"). Neat idea.



More information about the Python-list mailing list