debugpythonMinor
Failing fast by raising an exception with a custom message
Viewed 0 times
fastexceptionraisingwithfailingmessagecustom
Problem
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.
Output:
from functools import wraps
def 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 / b
test_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 2
0.5
Trying to divide 20 from 5
4.0
Trying to divide 3 from 0
Traceback (most recent call last):
File "", line 30, in
File "", line 16, in on_fail_wrapper
Exception: Division Failed
Because: division by zero
Solution
-
Instead of handling just
-
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:
Note that in above code I didn't use something simple like:
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:
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 wraps
def 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 / b
test_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 0
Traceback (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 / b
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/ashwini/py/so.py", line 26, in
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 Failed
Because: division by zeroCode Snippets
from functools import wraps
def 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 / b
test_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))except exception as e:
raise exception("{0}\nBecause: {1}".format(message, e))Trying to divide 3 from 0
Traceback (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 / b
ZeroDivisionError: division by zero
During 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 Failed
Because: division by zeroContext
StackExchange Code Review Q#80730, answer score: 5
Revisions (0)
No revisions yet.