The Hidden Dangers of Python Decorators
Overview
Prerequisites
To follow this guide, you should have a solid understanding of

Key Libraries & Tools
- functools: A standard library module used to preserve function metadata.
- Hydra: A framework for configuring complex applications that relies heavily on decorators.
- FastAPI: A modern web framework where decorators define routes and logic.
- SlowAPI: A rate-limiting library forFastAPI.
Code Walkthrough
A decorator is simply a function that returns another function. Here is how you build a standard logging decorator:
import functools
from typing import Callable, Any
def log_call(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_call
def add(a: int, b: int) -> int:
return a + b
The log_call function receives the target function, wraps it in a wrapper that adds the print statement, and returns that wrapper. Using @functools.wraps(func) is vital; without it, the function's name and docstring would be overwritten by the wrapper's metadata.
Syntax Notes
Decorators follow a "bottom-to-top" evaluation order. If you stack multiple decorators, the one closest to the function executes first, effectively wrapping the function. The decorator above it then wraps that combined result. This stacking logic often leads to logic errors in authentication or caching layers.
Practical Examples
In Request object, just to satisfy the decorator’s internal requirements. This creates "dead code" that confuses type checkers and other developers.
Tips & Gotchas
- Order Matters: Putting a cache decorator above an authentication decorator might accidentally cache sensitive data for unauthenticated users.
- Signature Breakage: Libraries like Hydrainject arguments into functions at runtime, making static analysis nearly impossible.
- Debugging Hell: Errors inside a decorator often produce confusing stack traces that point away from the actual bug in your logic.