Search code examples
pythonexceptiondecoratorpython-decorators

How to detect missing arguments in decorator?


I would like to define a decorator with a parameter that raises an error if the parameter is missing.

Here's a naive attempt on a simplified example:

def decorator_with_arg(a=None):

    if a is None :
        raise ValueError("Missing argument in decorator")

    def decorator(func):

        def wrapped_func(x):
            return func(x+ a)

        return wrapped_func

    return decorator

But when I use this decorator without a parameter it's not raising any errors:

@decorator_with_arg
def simple_func(x):
    return 2*x

simple_func(1)

How can I raise an exception?


Solution

  • You are not using your decorator correctly, in your code simple_func(1) will just return wrapped_func, because @decorator_with_arg will simply do:

    simple_func = decorator_with_arg(simple_func)
    #                                ^ this is passing a=simple_func
    # now simple_func is the decorator function defined inside decorator_with_arg
    

    You need to call your decorator_with_arg in order for it to return decorator which will then be used to decorate the function:

    @decorator_with_arg(100)
    def simple_func(x):
        return 2*x
    
    print(simple_func(1)) # 202
    

    In any case, if you want to make an argument mandatory, simply declare it without a default value:

    def decorator_with_arg(a):
        # ...
    

    And remove the if a is None check.


    If you want to avoid mistakes in using @decorator_with_arg instead of @decorator_with_arg(), you can add a check to ensure a is not a function:

    def decorator_with_arg(a):
        if callable(a):
            raise TypeError("Incorrect use of decorator")
        
        def decorator(func):
            def wrapped_func(x):
                return func(x + a)
            return wrapped_func
        return decorator
    
    
    @decorator_with_arg
    def func():
        return 1
    # TypeError: Incorrect use of decorator
    
    
    @decorator_with_arg(123)
    def func():
        return 1
    # All fine