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 
<https://rinaarts.com/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.

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/269eb112-6de3-4860-aaeb-fadab60585ean%40googlegroups.com.

Reply via email to