Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Accessing default value of decorator argument in Python

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

@_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).

>Solution :

add this code nonlocal msg.

def _exception_handler(msg=""):
    """Custom decorator to return a more informative error message"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            nonlocal msg
            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
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading