Exception Handling With Decorators

29 views
Skip to first unread message

Thomas Passin

unread,
Oct 6, 2022, 12:57:58 AM10/6/22
to leo-editor
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

There are various ways to write decorators; for example, they can be done without using functools.  I borrowed this particular formulation from Declutter python code with error handling decorators.

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.

Edward K. Ream

unread,
Oct 6, 2022, 6:29:00 AM10/6/22
to leo-e...@googlegroups.com
On Wed, Oct 5, 2022 at 11:58 PM Thomas Passin <tbp1...@gmail.com> wrote:
I wondered recently how practical it would be to do exception handling with decorators instead of try...except blocks. 

An interesting idea. However, the granularity of the try/except blocks is the entire decorated function, which is often not what is desired.

Edward

Thomas Passin

unread,
Oct 6, 2022, 8:37:45 AM10/6/22
to leo-editor
Right.  The decorator approach doesn't seem to be a general way to go about it.
Reply all
Reply to author
Forward
0 new messages