functools.wraps
Overview
When you write a decorator, the returned wrapper function replaces the original function. By default, this wrapper hides the original function's metadata (like its `__name__` and `__doc__`). Using `@functools.wraps(func)` on the wrapper copies the original function's metadata to the wrapper. Think of it like wearing a costume: without `@wraps`, people see the costume and forget who is inside; with `@wraps`, you wear a name tag over the costume so your real identity is preserved.
Web frameworks and documentation generators rely on function metadata to register routes and build docs. If decorators obscure this, frameworks fail to map URLs correctly.
Where used: FastAPI routes, Flask views, Celery tasks
Why learn this
- Ensuring your decorators don't break web frameworks that inspect function names.
- Generating accurate documentation for decorated functions.
Code walkthrough
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper():
return func()
return wrapper
@my_decorator
def real_function():
pass
print(real_function.__name__)
Focus: print(real_function.__name__)
Common mistakes
- Missing @wraps on decorators: Forgetting to decorate the inner wrapper with `@wraps(func)`. This causes the decorated function's `__name__` to become `'wrapper'`, breaking reflection.
Glossary
- metadata
- Information that describes other data, like a function's name or its documentation. Example: `func.__name__`
- reflection
- The ability of a program to examine and modify its own structure and behavior while it is running. Example: `inspect.signature(func)`
Recall questions
- What happens to a function's `__name__` attribute if you decorate it with a custom decorator that doesn't use `functools.wraps`?
- Why do frameworks like FastAPI or Flask require decorators to use `functools.wraps`?
Understanding checks
What does this script print?
wrapper
Because `greet` is replaced by `wrapper`, its `__name__` becomes `'wrapper'`. Without `@wraps`, the original identity is lost.
Why would missing @wraps on a decorator cause a FastAPI application to fail when defining routes?
FastAPI uses the function's name and type hints to register routes and build documentation.
If a decorator hides the original function's metadata, FastAPI only sees the wrapper function's name (often literally 'wrapper') and signature, losing the actual route details.
Practice tasks
Fix a broken decorator
The provided `log_calls` decorator currently hides the original function's name and docstring. Import `functools` and use `@functools.wraps` to fix the decorator so that `calculate_tax.__name__` prints `'calculate_tax'`.
Challenge
Create a well-behaved timing decorator
Write a `time_it` decorator that measures execution time and prints it. It must use `functools.wraps` so the decorated function retains its `__doc__` and `__name__`. Test it on a function named `process_data`.
Continue learning
Previous: Decorators
Previous: Closures & LEGB scope