This is a script that utilizes lazy exception throwing. What I do is decorate a function that might throw an exception to throw an exception with a formatted string. from functools import wrapsdef on_fail(message):    def on_fail_dec(func):        @wraps(func)        def on_fail_wrapper(*args, **kw):            ret = None            success = True            message_addon =             try:                ret = func(*args, **kw)            except Exception as e:                success = False                message_addon = str(e)            # raise Exception if failed            if not success:                raise Exception({0}\nBecause: {1}.format(message, message_addon))            return ret        return on_fail_wrapper    return on_fail_dec# Usage :@on_fail(Division Failed)def divide(a, b):    return a / btest_cases = [(1, 2), (20, 5), (3, 0)]for a,b in test_cases:    print(Trying to divide {0} from {1}.format(a, b))    print(divide(a, b))Output:Trying to divide 1 from 20.5Trying to divide 20 from 54.0Trying to divide 3 from 0Traceback (most recent call last):  File <string>, line 30, in <module>  File <string>, line 16, in on_fail_wrapperException: Division FailedBecause: division by zero
Failing fast by raising an exception with a custom message
python;python 3.x;functional programming;error handling;meta programming
Instead of handling just Division Failed you should extend your function to handle any possible error, plus it's better if you pass the exception object along with the message to the decorator as well, by doing this you'll know exactly which error you're going to handle because a except Exception will handle any Exception not just what we actually intended it to.Second there's no need to use flag variables here, we can simply return the return value from the try-block and in case of exception we re-raise the exception after it is caught.Code:from functools import wrapsdef on_fail(exception, message):    def on_fail_dec(func):        @wraps(func)        def on_fail_wrapper(*args, **kw):            message_addon =             try:                # do something before                ret = func(*args, **kw)                # do something after                return ret            except exception as e:               message_addon = str(e)            raise exception({0}\nBecause: {1}.format(message, message_addon))        return on_fail_wrapper    return on_fail_dec@on_fail(ZeroDivisionError, Division Failed)def divide(a, b):    return a / btest_cases = [(1, 2), (20, 5), (3, 0)]for a, b in test_cases:    print(Trying to divide {0} from {1}.format(a, b))    print(divide(a, b))Note that in above code I didn't use something simple like:except exception as e:    raise exception({0}\nBecause: {1}.format(message, e))That's because Python 3 introduced something called exception chaining, which means if an exception occurs while handling another exception then the previous exception still remains available to us(Read PEP 3134 - PEP 3134 -- Exception Chaining and Embedded Tracebacks), but this is not useful here as it will end up raising something like:Trying to divide 3 from 0Traceback (most recent call last):  File /home/ashwini/py/so.py, line 10, in on_fail_wrapper    ret = func(*args, **kw)  File /home/ashwini/py/so.py, line 20, in divide    return a / bZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback (most recent call last):  File /home/ashwini/py/so.py, line 26, in <module>    print(divide(a, b))  File /home/ashwini/py/so.py, line 13, in on_fail_wrapper    raise exception({0}\nBecause: {1}.format(message, e))ZeroDivisionError: Division FailedBecause: division by zero