Musings: Using decorators to reduce duplicate exception handling
J Kenneth King
james at agentultra.com
Thu Feb 19 16:28:52 CET 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.
> 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):
> 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.
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
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