I wondered recently how practical it would be to do exception handling with decorators instead of try...except blocks. This would simplify some functions and methods, and make them easier to read. I found some posts on the Internet about this, and the upshot is that yes, it can be done at least in some cases.
Here's an example from some code from my GF4 project. It's a dialog box that evaluates and returns a floating point number (which can be a Python expression). This part of the code validates the input:
The original code:
def validate(self):
try:
first= float(eval(self.e1.get()))
self.result = first
return True
except ValueError:
tkMessageBox.showwarning(
"Bad input",
"Illegal value, try again"
)
return False
except SyntaxError:
tkMessageBox.showwarning(
"Syntax Error",
"Fix syntax"
)
return False
except Exception as e:
tkMessageBox.showwarning(
'Error',
"Try again ...%s" % e
)
return False
That's a lot of boiler plate, and not very readable. Here's the code using decorators:
@val_error(Exception, 'Error ...', "Try again")
@val_error(ValueError, "Bad input","Illegal value, try again")
@val_error(SyntaxError, "Syntax Error", "Fix syntax")
def validate(self):
first= float(eval(self.e1.get()))
self.result = first
return True
This is certainly easier to read and understand. It looks like it's success-oriented code, but the decorators handle the failure cases. Of course, there has to be complication somewhere, and in this case it resides in the val_error() decorator. Decorators are somewhat abstract and so tend to be harder to understand. But they can simplify the using code. Here's the decorator:
def val_error (except_type, msg1, msg2):
def wrapper(f):
@functools.wraps(f)
def inner(*args, **kwargs):
try:
return f(*args, **kwargs)
except except_type as e:
emsg = f'{e}'.split('(<string')[0]
tkMessageBox.showwarning(f'{msg1}',
f'{emsg}: {msg2}')
return False
return inner
return wrapper
Whether this approach is worth doing depends on whether the decorator would be reused some number of times, and on how clever one can be about constructing the decorator. In the example above, I have a number of similar variations of the basic dialog, which can all use the same decorator, so it seems to be very useful. Your mileage may vary.