I am trying to write a custom Python decorator which wraps the decorated function in a try ... except
block and adds a message with additional context to make debugging easier.
Based on different resources (see here and here for example) I so far built the following:
def _exception_handler(msg):
"""Custom decorator to return a more informative error message"""
def decorator(func):
def wrapper(*args, **kwargs):
try:
# this is the actual decorated function
func(*args, **kwargs)
except Exception as e:
# we catch the exception and raise a new one with the custom
# message and the original exception as cause
raise Exception(f"{msg}: {e}") from e
return wrapper
return decorator
This works as expected - if I run:
@_exception_handler("Foo")
def test():
raise ValueError("Bar")
test()
This returns:
Exception: Foo: Bar
Now, I do not always want to pass a custom msg
because that's sometimes a bit redundant. So I set a default value of msg=""
and I want to check if msg==""
and if that is the case I would just like to re-create the msg
inside the decorator based on the function name, something like msg = f"An error occurred in {func.__name__}"
.
I would then like to use the decorator without any msg
argument. I do not care about the empty parantheses, using @_exception_handler()
is perfectly fine for me.
But somehow this does not seem to work:
def _exception_handler(msg=""):
"""Custom decorator to return a more informative error message"""
def decorator(func):
def wrapper(*args, **kwargs):
try:
# this is the actual decorated function
func(*args, **kwargs)
except Exception as e:
if msg=="":
# if no custom message is passed, we just use the function name
msg = f"An error occurred in {func.__name__}"
# we catch the exception and raise a new one with the message
# and the original exception as cause
raise Exception(f"{msg}: {e}") from e
return wrapper
return decorator
If I run:
@_exception_handler()
def test():
raise ValueError("Bar")
test()
I get
UnboundLocalError: local variable 'msg' referenced before assignment
If I put global message
right below the def decorator(func):
line, I get the same errors. If I put it below the def wrapper(*args, **kwargs):
, I instead get:
NameError: name 'msg' is not defined
Any ideas how I can get this to work? If possible, I would like to avoid any third-party modules such as wrapt
. Using wraps
from functools
from the standard library is fine of course (although I did not have any luck with that so far either).