-
Notifications
You must be signed in to change notification settings - Fork 0
Description
FastAPI incorrectly detects async/sync with functools.wraps decorators
Problem Description
When using functools.wraps or functools.partial on functions that serve as FastAPI dependencies or path operation functions (endpoints), FastAPI may incorrectly determine whether the function should be executed as synchronous or asynchronous. This results in runtime errors when the decorated functions are called.
Current Behavior
Starting in FastAPI version 0.123.5, decorated functions using functools.wraps cause execution errors. For example:
- A synchronous function is wrapped with an async decorator using
@functools.wraps - When the endpoint is called, FastAPI incorrectly treats the function as synchronous
- At runtime, this produces a
ValueErrorabout coroutine objects not being iterable - Users receive "Internal Server Error" responses
Example scenario:
from functools import wraps
from fastapi import FastAPI
app = FastAPI()
def my_decorator(func):
@wraps(func)
async def wrapper():
func()
return 'OK'
return wrapper
@app.get('/')
@my_decorator
def index():
print('Hello!')
# Accessing this endpoint results in:
# ValueError: [TypeError("'coroutine' object is not iterable"), ...]Scope of Issue
This problem affects:
- Regular functions (both sync and async)
- Generator functions (both sync and async)
- Functions decorated with
functools.wraps - Functions wrapped with
functools.partial - Callable class instances (objects with
__call__methods) - Any combination of the above
The issue occurs when these decorated functions are used as:
- Path operation functions (endpoint handlers)
- Dependency injection functions
Expected Behavior
FastAPI should correctly identify whether a decorated function needs to be awaited, regardless of how many layers of decorators are applied. If either the original function OR any wrapper in the decorator chain is asynchronous, FastAPI should treat it as async and await it properly. The framework should handle all combinations of:
- Sync functions with async wrappers
- Async functions with sync wrappers
- Functions using
functools.wrapsorfunctools.partial - Generator and async generator functions
- Callable class instances with decorated
__call__methods
This functionality worked correctly in FastAPI versions prior to 0.123.5 and should be restored.