functools.wraps is a decorator used inside decorator definitions to preserve the wrapped function's metadata — its __name__, __doc__, __module__, __qualname__, __annotations__, and __dict__. Without @wraps, a decorated function's name and docstring would be replaced by those of the wrapper function, making debugging and introspection confusing.
The usage pattern is: inside your decorator, define the wrapper function and decorate it with @functools.wraps(original_func). For example: def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): ... return wrapper. The @wraps decorator copies the original function's attributes onto the wrapper, so help(decorated_function) shows the original docstring and decorated_function.__name__ returns the original name.
@wraps also sets a __wrapped__ attribute on the wrapper, pointing to the original function. This allows tools to "unwrap" decorated functions (via inspect.unwrap()) for testing or introspection. Using @functools.wraps is considered mandatory best practice when writing decorators — omitting it is a common beginner mistake that makes decorated functions harder to debug and breaks tools that rely on function metadata.
Discussed in:
- Chapter 17: Decorators, Generators, and Iterators — functools.wraps