How does the wraps' function of the functools.py module work?

How does the wraps' function of the functools.py module work?

I came across the following code snippet in Python:

def log_function(argument):
    def decorator_factory(func):
        @functools.wraps(func)
        def handler_decorator(*args, **kwargs):
            time_start = time()        
            print(f"Foi iniciada a execução: {argument}")
            result = func(*args, **kwargs)
            time_end = time()
            print(
                f"Foi finalizada a execução: {argument}"
                f" demorou {(time_end - time_start):.4f} segundos"
            )
            return result

        return handler_decorator

    return decorator_factory

Basically, this is a decorator used to provide log for calling some function, ok?!

Example:

Captura de Tela 2021-10-16 às 15.41.35.png

But these wraps there? @functools.wraps(func)

The @functools module, provides tools for working with functions and object calls, the wraps() this takes a function used in a decorator and adds the functionality of copying over the function name, docstring, arguments list, etc.

Example:

Let's comment the excerpt @functools.wraps(func)

def log_function(argument):
    def decorator_factory(func):
        # @functools.wraps(func)
        def handler_decorator(*args, **kwargs):
            time_start = time()        
            print(f"Foi iniciada a execução: {argument}")
            result = func(*args, **kwargs)
            time_end = time()
            print(
                f"Foi finalizada a execução: {argument}"
                f" demorou {(time_end - time_start):.4f} segundos"
            )
            return result

        return handler_decorator

    return decorator_factory

When we run the test function we get the same operation, however, notice that there was a switch and the f() function is now the handler_decorator function:

Captura de Tela 2021-10-16 às 15.55.31.png

As we replace the @functools.wraps(func) snippet, notice that we continue with the same f() function without switching to the handler_decorator function.

Captura de Tela 2021-10-16 às 15.59.57.png

Conclusion:

To avoid problems when creating our decorators, such as losing information from the calling function, we use this wraps() function. ;)