import inspect import sys from functools import wraps from typing import AsyncGenerator, Generator import pytest from fastapi import Depends, FastAPI from fastapi.concurrency import iterate_in_threadpool, run_in_threadpool from fastapi.testclient import TestClient if sys.version_info >= (3, 13): # pragma: no cover from inspect import iscoroutinefunction else: # pragma: no cover from asyncio import iscoroutinefunction def noop_wrap(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper def noop_wrap_async(func): if inspect.isgeneratorfunction(func): @wraps(func) async def gen_wrapper(*args, **kwargs): async for item in iterate_in_threadpool(func(*args, **kwargs)): yield item return gen_wrapper elif inspect.isasyncgenfunction(func): @wraps(func) async def async_gen_wrapper(*args, **kwargs): async for item in func(*args, **kwargs): yield item return async_gen_wrapper @wraps(func) async def wrapper(*args, **kwargs): if inspect.isroutine(func) and iscoroutinefunction(func): return await func(*args, **kwargs) if inspect.isclass(func): return await run_in_threadpool(func, *args, **kwargs) dunder_call = getattr(func, "__call__", None) # noqa: B004 if iscoroutinefunction(dunder_call): return await dunder_call(*args, **kwargs) return await run_in_threadpool(func, *args, **kwargs) return wrapper class ClassInstanceDep: def __call__(self): return True class_instance_dep = ClassInstanceDep() wrapped_class_instance_dep = noop_wrap(class_instance_dep) wrapped_class_instance_dep_async_wrapper = noop_wrap_async(class_instance_dep) class ClassInstanceGenDep: def __call__(self): yield True class_instance_gen_dep = ClassInstanceGenDep() wrapped_class_instance_gen_dep = noop_wrap(class_instance_gen_dep) class ClassInstanceWrappedDep: @noop_wrap def __call__(self): return True class_instance_wrapped_dep = ClassInstanceWrappedDep() class ClassInstanceWrappedAsyncDep: @noop_wrap_async def __call__(self): return True class_instance_wrapped_async_dep = ClassInstanceWrappedAsyncDep() class ClassInstanceWrappedGenDep: @noop_wrap def __call__(self): yield True class_instance_wrapped_gen_dep = ClassInstanceWrappedGenDep() class ClassInstanceWrappedAsyncGenDep: @noop_wrap_async def __call__(self): yield True class_instance_wrapped_async_gen_dep = ClassInstanceWrappedAsyncGenDep() class ClassDep: def __init__(self): self.value = True wrapped_class_dep = noop_wrap(ClassDep) wrapped_class_dep_async_wrapper = noop_wrap_async(ClassDep) class ClassInstanceAsyncDep: async def __call__(self): return True class_instance_async_dep = ClassInstanceAsyncDep() wrapped_class_instance_async_dep = noop_wrap(class_instance_async_dep) wrapped_class_instance_async_dep_async_wrapper = noop_wrap_async( class_instance_async_dep ) class ClassInstanceAsyncGenDep: async def __call__(self): yield True class_instance_async_gen_dep = ClassInstanceAsyncGenDep() wrapped_class_instance_async_gen_dep = noop_wrap(class_instance_async_gen_dep) class ClassInstanceAsyncWrappedDep: @noop_wrap async def __call__(self): return True class_instance_async_wrapped_dep = ClassInstanceAsyncWrappedDep() class ClassInstanceAsyncWrappedAsyncDep: @noop_wrap_async async def __call__(self): return True class_instance_async_wrapped_async_dep = ClassInstanceAsyncWrappedAsyncDep() class ClassInstanceAsyncWrappedGenDep: @noop_wrap async def __call__(self): yield True class_instance_async_wrapped_gen_dep = ClassInstanceAsyncWrappedGenDep() class ClassInstanceAsyncWrappedGenAsyncDep: @noop_wrap_async async def __call__(self): yield True class_instance_async_wrapped_gen_async_dep = ClassInstanceAsyncWrappedGenAsyncDep() app = FastAPI() # Sync wrapper @noop_wrap def wrapped_dependency() -> bool: return True @noop_wrap def wrapped_gen_dependency() -> Generator[bool, None, None]: yield True @noop_wrap async def async_wrapped_dependency() -> bool: return True @noop_wrap async def async_wrapped_gen_dependency() -> AsyncGenerator[bool, None]: yield True @app.get("/wrapped-dependency/") async def get_wrapped_dependency(value: bool = Depends(wrapped_dependency)): return value @app.get("/wrapped-gen-dependency/") async def get_wrapped_gen_dependency(value: bool = Depends(wrapped_gen_dependency)): return value @app.get("/async-wrapped-dependency/") async def get_async_wrapped_dependency(value: bool = Depends(async_wrapped_dependency)): return value @app.get("/async-wrapped-gen-dependency/") async def get_async_wrapped_gen_dependency( value: bool = Depends(async_wrapped_gen_dependency), ): return value @app.get("/wrapped-class-instance-dependency/") async def get_wrapped_class_instance_dependency( value: bool = Depends(wrapped_class_instance_dep), ): return value @app.get("/wrapped-class-instance-async-dependency/") async def get_wrapped_class_instance_async_dependency( value: bool = Depends(wrapped_class_instance_async_dep), ): return value @app.get("/wrapped-class-instance-gen-dependency/") async def get_wrapped_class_instance_gen_dependency( value: bool = Depends(wrapped_class_instance_gen_dep), ): return value @app.get("/wrapped-class-instance-async-gen-dependency/") async def get_wrapped_class_instance_async_gen_dependency( value: bool = Depends(wrapped_class_instance_async_gen_dep), ): return value @app.get("/class-instance-wrapped-dependency/") async def get_class_instance_wrapped_dependency( value: bool = Depends(class_instance_wrapped_dep), ): return value @app.get("/class-instance-wrapped-async-dependency/") async def get_class_instance_wrapped_async_dependency( value: bool = Depends(class_instance_wrapped_async_dep), ): return value @app.get("/class-instance-async-wrapped-dependency/") async def get_class_instance_async_wrapped_dependency( value: bool = Depends(class_instance_async_wrapped_dep), ): return value @app.get("/class-instance-async-wrapped-async-dependency/") async def get_class_instance_async_wrapped_async_dependency( value: bool = Depends(class_instance_async_wrapped_async_dep), ): return value @app.get("/class-instance-wrapped-gen-dependency/") async def get_class_instance_wrapped_gen_dependency( value: bool = Depends(class_instance_wrapped_gen_dep), ): return value @app.get("/class-instance-wrapped-async-gen-dependency/") async def get_class_instance_wrapped_async_gen_dependency( value: bool = Depends(class_instance_wrapped_async_gen_dep), ): return value @app.get("/class-instance-async-wrapped-gen-dependency/") async def get_class_instance_async_wrapped_gen_dependency( value: bool = Depends(class_instance_async_wrapped_gen_dep), ): return value @app.get("/class-instance-async-wrapped-gen-async-dependency/") async def get_class_instance_async_wrapped_gen_async_dependency( value: bool = Depends(class_instance_async_wrapped_gen_async_dep), ): return value @app.get("/wrapped-class-dependency/") async def get_wrapped_class_dependency(value: ClassDep = Depends(wrapped_class_dep)): return value.value @app.get("/wrapped-endpoint/") @noop_wrap def get_wrapped_endpoint(): return True @app.get("/async-wrapped-endpoint/") @noop_wrap async def get_async_wrapped_endpoint(): return True # Async wrapper @noop_wrap_async def wrapped_dependency_async_wrapper() -> bool: return True @noop_wrap_async def wrapped_gen_dependency_async_wrapper() -> Generator[bool, None, None]: yield True @noop_wrap_async async def async_wrapped_dependency_async_wrapper() -> bool: return True @noop_wrap_async async def async_wrapped_gen_dependency_async_wrapper() -> AsyncGenerator[bool, None]: yield True @app.get("/wrapped-dependency-async-wrapper/") async def get_wrapped_dependency_async_wrapper( value: bool = Depends(wrapped_dependency_async_wrapper), ): return value @app.get("/wrapped-gen-dependency-async-wrapper/") async def get_wrapped_gen_dependency_async_wrapper( value: bool = Depends(wrapped_gen_dependency_async_wrapper), ): return value @app.get("/async-wrapped-dependency-async-wrapper/") async def get_async_wrapped_dependency_async_wrapper( value: bool = Depends(async_wrapped_dependency_async_wrapper), ): return value @app.get("/async-wrapped-gen-dependency-async-wrapper/") async def get_async_wrapped_gen_dependency_async_wrapper( value: bool = Depends(async_wrapped_gen_dependency_async_wrapper), ): return value @app.get("/wrapped-class-instance-dependency-async-wrapper/") async def get_wrapped_class_instance_dependency_async_wrapper( value: bool = Depends(wrapped_class_instance_dep_async_wrapper), ): return value @app.get("/wrapped-class-instance-async-dependency-async-wrapper/") async def get_wrapped_class_instance_async_dependency_async_wrapper( value: bool = Depends(wrapped_class_instance_async_dep_async_wrapper), ): return value @app.get("/wrapped-class-dependency-async-wrapper/") async def get_wrapped_class_dependency_async_wrapper( value: ClassDep = Depends(wrapped_class_dep_async_wrapper), ): return value.value @app.get("/wrapped-endpoint-async-wrapper/") @noop_wrap_async def get_wrapped_endpoint_async_wrapper(): return True @app.get("/async-wrapped-endpoint-async-wrapper/") @noop_wrap_async async def get_async_wrapped_endpoint_async_wrapper(): return True client = TestClient(app) @pytest.mark.parametrize( "route", [ "/wrapped-dependency/", "/wrapped-gen-dependency/", "/async-wrapped-dependency/", "/async-wrapped-gen-dependency/", "/wrapped-class-instance-dependency/", "/wrapped-class-instance-async-dependency/", "/wrapped-class-instance-gen-dependency/", "/wrapped-class-instance-async-gen-dependency/", "/class-instance-wrapped-dependency/", "/class-instance-wrapped-async-dependency/", "/class-instance-async-wrapped-dependency/", "/class-instance-async-wrapped-async-dependency/", "/class-instance-wrapped-gen-dependency/", "/class-instance-wrapped-async-gen-dependency/", "/class-instance-async-wrapped-gen-dependency/", "/class-instance-async-wrapped-gen-async-dependency/", "/wrapped-class-dependency/", "/wrapped-endpoint/", "/async-wrapped-endpoint/", "/wrapped-dependency-async-wrapper/", "/wrapped-gen-dependency-async-wrapper/", "/async-wrapped-dependency-async-wrapper/", "/async-wrapped-gen-dependency-async-wrapper/", "/wrapped-class-instance-dependency-async-wrapper/", "/wrapped-class-instance-async-dependency-async-wrapper/", "/wrapped-class-dependency-async-wrapper/", "/wrapped-endpoint-async-wrapper/", "/async-wrapped-endpoint-async-wrapper/", ], ) def test_class_dependency(route): response = client.get(route) assert response.status_code == 200, response.text assert response.json() is True