In Python, a higher-order function decorator is a function that takes another function (or class) as an argument and returns a new function (or a new modified class). Decorators are often used to implement the "wrapping" pattern, allowing additional logic (such as logging, caching, permission checks, etc.) to be added to existing functions without changing their original code.
To preserve the name, documentation, and other metadata of the original function, it is recommended to use the functools.wraps function:
import functools def log_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print(f'Calling {func.__name__}') return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): """Add two numbers""" return a + b print(add(2, 3)) # Output: Calling add 5 print(add.__name__) # Output: add print(add.__doc__) # Output: Add two numbers
Key point: without functools.wraps, the wrapped function will lose its name, documentation, and other original metadata, which negatively impacts debugging and auto-documentation.
If a function is decorated without using functools.wraps, what will happen to the name and doc attributes of the function?
Answer: They will be inherited from the inner wrapper function (usually 'wrapper'), and you will lose the original metadata.
def simple_decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @simple_decorator def f(): """This is docstring""" pass print(f.__name__) # Output: 'wrapper' (NOT 'f') print(f.__doc__) # Output: None (not 'This is docstring')
Story
In a project, a complex system of decorators for logging API endpoints was implemented, but functools.wraps was not applied. As a result, auto generation of documentation (Swagger/OpenAPI) and introspection tools displayed the names of all endpoints as 'wrapper', with documentation missing. This significantly complicated support, testing, and maintenance.
Story
When writing unit tests using pytest, there was a failure in test auto-discovery: test functions decorated with their own decorators without wraps were not found because their name was incorrect. The reason is that pytest searches for functions by name.
Story
When tracing exception stack traces, the stack trace always pointed to "wrapper", making it impossible to understand which specific function raised the error, as root metadata was lost due to the absence of functools.wraps in custom decorators.