Musings: Using decorators to reduce duplicate exception handling

J

J Kenneth King

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. However, this obviously will duplicate code all over the
place. There are only ever two exceptions that the module itself will
throw: xmlrpclib.ProtocolError and xmlrpclib.Fault -- both very useful,
but not show stoppers.

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):
"""
A decorator to catch typical xmlrpclib exceptions
"""
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except xmlrpclib.ProtocolError, e:
print "There was a problem communicating with the server."
print "URL: %s" % e.url
print "Headers: %s" % e.headers
print "Error code: %d" % e.errcode
print "Error message: %s" % e.errmsg
print "Please file a report with the TracShell developers."
pass
except xmlrpclib.Fault, e:
print "A fault ocurred"
print "Fault code: %d" % e.faultCode
print "Fault string: %s" % e.faultString
print "If you think this message is the result of an error,"
print "please file a report with the TracShell developers."
pass
return wrapped

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.

I was just curious if other Pythonistas out there used the same idea or
have a better one. Thoughts?
 
G

Gabriel Genellina

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. However, this obviously will duplicate code all over the
place. There are only ever two exceptions that the module itself will
throw: xmlrpclib.ProtocolError and xmlrpclib.Fault -- both very useful,
but not show stoppers.

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):
"""
A decorator to catch typical xmlrpclib exceptions
"""
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except xmlrpclib.ProtocolError, e:
print "There was a problem communicating with the server."
print "URL: %s" % e.url
print "Headers: %s" % e.headers
print "Error code: %d" % e.errcode
print "Error message: %s" % e.errmsg
print "Please file a report with the TracShell developers."
pass
except xmlrpclib.Fault, e:
print "A fault ocurred"
print "Fault code: %d" % e.faultCode
print "Fault string: %s" % e.faultString
print "If you think this message is the result of an error,"
print "please file a report with the TracShell developers."
pass
return wrapped

I don't like the idea of "hiding" an exception. The caller code doesn't
know an exception occurred, and just continue doing its work, with bogus
results... is this what you want? Also, you don't include the stack trace
- and usually it contains very valuable information. When your users start
"filing a report with the TracShell developers" and you feel clueless, a
stack trace is important (you don't have to show it on screen - a log file
is even better).

If all you want is to customize the error message, use sys.except_hook

Looking into the code, those "pass" statement are useless; and error
messages are usually written to stderr instead of stdout.
 
J

J Kenneth King

Gabriel Genellina said:
En Tue, 17 Feb 2009 21:12:57 -0200, J Kenneth King


I don't like the idea of "hiding" an exception. The caller code
doesn't know an exception occurred, and just continue doing its work,
with bogus results... is this what you want? Also, you don't include
the stack trace - and usually it contains very valuable
information. When your users start "filing a report with the
TracShell developers" and you feel clueless, a stack trace is
important (you don't have to show it on screen - a log file is even
better).

If all you want is to customize the error message, use sys.except_hook

Looking into the code, those "pass" statement are useless; and error
messages are usually written to stderr instead of stdout.

Thanks for the ideas.

I haven't actually used this pattern in any projects before, I was just
looking for a way to reduce the number of common try/except statements I
needed to have in my code; especially when they all looked exactly the
same and were catching every single xml-rpc call littered throughout my
class.

sys.except_hook looks more like what I was aiming for. I like Lisp's
"restarts," where I can define error condition handlers in one place
without having to litter my code with error handling statements.

Cheers.
 
D

Diez B. Roggisch

J said:
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. However, this obviously will duplicate code all over the
place. There are only ever two exceptions that the module itself will
throw: xmlrpclib.ProtocolError and xmlrpclib.Fault -- both very useful,
but not show stoppers.

I think the answer depends a lot on the actual error handling performed.
If you want retries, either immediately, or with some larger prologue,
it might make sense to abstract that away (I would have suggested the
context-manager thing myself, but Cameron did that already).

But if you essentially want to abort in all cases, I'd just push the
handling upwards, and rather improve the error reporting itself, e.g. by
storing away the trace in a temporary file the user can send you.

Diez
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,799
Latest member
Mercury_Dev

Latest Threads

Top