Exceptions for the whole class
Question:
I’m writing a program in Python, and nearly every method im my class is written like this:
def someMethod(self):
try:
#...
except someException:
#in case of exception, do something here
#e.g display a dialog box to inform the user
#that he has done something wrong
As the class grows, it is a little bit annoying to write the same try-except block over and over. Is it possible to create some sort of ‘global’ exception for the whole class? What’s the recommended way in Python to deal with this?
Answers:
I can think of two options:
- Write a decorator that can wrap each method in the
try
block.
- Write a “dispatcher” method that calls the appropriate method inside a
try
block, then call that method instead of the individual ones. That is, instead of calling obj.someMethod()
, obj.otherMethod
, you call obj.dispatch('someMethod')
or obj.dispatch('otherMethod')
, where dispatch
is a wrapper that contains the try
block.
Your approach seems like a bit of a strange design, though. It might make more sense to have the dialog-box stuff in some other part of the code, some higher-level event loop that catches errors and displays messages about them.
Write one or more exception handler functions that, given a function and the exception raised in it, does what you want to do (e.g. displays an alert). If you need more than one, write them.
def message(func, e):
print "Exception", type(e).__name__, "in", func.__name__
print str(e)
Now write a decorator that applies a given handler to a called function:
import functools
def handle_with(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda f, e: None
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exceptions or Exception as e:
return handler(func, e)
else:
e = None
finally:
cleanup(func, e)
return wrapper
return decorator
This only captures the exceptions you specify. If you don’t specify any, Exception
is caught. Additionally, the first argument can be a tuple (or other sequence) of two handler functions; the second handler, if given, is called in a finally
clause. The value returned from the primary handler is returned as the value of the function call.
Now, given the above, you can write:
@handle_with(message, TypeError, ValueError)
def add(x, y):
return x + y
You could also do this with a context manager:
from contextlib import contextmanager
@contextmanager
def handler(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda e: None
try:
yield
except exceptions or Exception as e:
handler(e)
else:
e = None
finally:
cleanup(e)
Now you can write:
def message(e):
print "Exception", type(e).__name__
print str(e)
def add(x, y):
with handler(message, TypeError, ValueError):
return x + y
Note that the context manager doesn’t know what function it’s in (you can find this out, sorta, using inspect
, though this is "magic" so I didn’t do it) so it gives you a little less useful information. Also, the context manager doesn’t give you the opportunity to return anything in your handler.
I’m writing a program in Python, and nearly every method im my class is written like this:
def someMethod(self):
try:
#...
except someException:
#in case of exception, do something here
#e.g display a dialog box to inform the user
#that he has done something wrong
As the class grows, it is a little bit annoying to write the same try-except block over and over. Is it possible to create some sort of ‘global’ exception for the whole class? What’s the recommended way in Python to deal with this?
I can think of two options:
- Write a decorator that can wrap each method in the
try
block. - Write a “dispatcher” method that calls the appropriate method inside a
try
block, then call that method instead of the individual ones. That is, instead of callingobj.someMethod()
,obj.otherMethod
, you callobj.dispatch('someMethod')
orobj.dispatch('otherMethod')
, wheredispatch
is a wrapper that contains thetry
block.
Your approach seems like a bit of a strange design, though. It might make more sense to have the dialog-box stuff in some other part of the code, some higher-level event loop that catches errors and displays messages about them.
Write one or more exception handler functions that, given a function and the exception raised in it, does what you want to do (e.g. displays an alert). If you need more than one, write them.
def message(func, e):
print "Exception", type(e).__name__, "in", func.__name__
print str(e)
Now write a decorator that applies a given handler to a called function:
import functools
def handle_with(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda f, e: None
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except exceptions or Exception as e:
return handler(func, e)
else:
e = None
finally:
cleanup(func, e)
return wrapper
return decorator
This only captures the exceptions you specify. If you don’t specify any, Exception
is caught. Additionally, the first argument can be a tuple (or other sequence) of two handler functions; the second handler, if given, is called in a finally
clause. The value returned from the primary handler is returned as the value of the function call.
Now, given the above, you can write:
@handle_with(message, TypeError, ValueError)
def add(x, y):
return x + y
You could also do this with a context manager:
from contextlib import contextmanager
@contextmanager
def handler(handler, *exceptions):
try:
handler, cleanup = handler
except TypeError:
cleanup = lambda e: None
try:
yield
except exceptions or Exception as e:
handler(e)
else:
e = None
finally:
cleanup(e)
Now you can write:
def message(e):
print "Exception", type(e).__name__
print str(e)
def add(x, y):
with handler(message, TypeError, ValueError):
return x + y
Note that the context manager doesn’t know what function it’s in (you can find this out, sorta, using inspect
, though this is "magic" so I didn’t do it) so it gives you a little less useful information. Also, the context manager doesn’t give you the opportunity to return anything in your handler.