functools.wraps

RoadmapsPython Backend

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

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

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

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

Return to Python Backend Roadmap