(
func: Callable[P, Awaitable[R]]
)
| 114 | ) |
| 115 | |
| 116 | def wrapper( |
| 117 | func: Callable[P, Awaitable[R]] |
| 118 | ) -> Callable[P, Awaitable[Union[R, Response]]]: |
| 119 | # get_typed_signature ensures that any forward references are resolved first |
| 120 | wrapped_signature = get_typed_signature(func) |
| 121 | to_inject: List[Parameter] = [] |
| 122 | request_param = _locate_param(wrapped_signature, injected_request, to_inject) |
| 123 | response_param = _locate_param(wrapped_signature, injected_response, to_inject) |
| 124 | return_type = get_typed_return_annotation(func) |
| 125 | |
| 126 | @wraps(func) |
| 127 | async def inner(*args: P.args, **kwargs: P.kwargs) -> Union[R, Response]: |
| 128 | nonlocal coder |
| 129 | nonlocal expire |
| 130 | nonlocal key_builder |
| 131 | |
| 132 | async def ensure_async_func(*args: P.args, **kwargs: P.kwargs) -> R: |
| 133 | """Run cached sync functions in thread pool just like FastAPI.""" |
| 134 | # if the wrapped function does NOT have request or response in |
| 135 | # its function signature, make sure we don't pass them in as |
| 136 | # keyword arguments |
| 137 | kwargs.pop(injected_request.name, None) |
| 138 | kwargs.pop(injected_response.name, None) |
| 139 | |
| 140 | if iscoroutinefunction(func): |
| 141 | # async, return as is. |
| 142 | # unintuitively, we have to await once here, so that caller |
| 143 | # does not have to await twice. See |
| 144 | # https://stackoverflow.com/a/59268198/532513 |
| 145 | return await func(*args, **kwargs) |
| 146 | else: |
| 147 | # sync, wrap in thread and return async |
| 148 | # see above why we have to await even although caller also awaits. |
| 149 | return await run_in_threadpool(func, *args, **kwargs) # type: ignore[arg-type] |
| 150 | |
| 151 | copy_kwargs = kwargs.copy() |
| 152 | request: Optional[Request] = copy_kwargs.pop(request_param.name, None) # type: ignore[assignment] |
| 153 | response: Optional[Response] = copy_kwargs.pop(response_param.name, None) # type: ignore[assignment] |
| 154 | |
| 155 | if _uncacheable(request): |
| 156 | return await ensure_async_func(*args, **kwargs) |
| 157 | |
| 158 | prefix = FastAPICache.get_prefix() |
| 159 | coder = coder or FastAPICache.get_coder() |
| 160 | expire = expire or FastAPICache.get_expire() |
| 161 | key_builder = key_builder or FastAPICache.get_key_builder() |
| 162 | backend = FastAPICache.get_backend() |
| 163 | cache_status_header = FastAPICache.get_cache_status_header() |
| 164 | |
| 165 | cache_key = key_builder( |
| 166 | func, |
| 167 | f"{prefix}:{namespace}", |
| 168 | request=request, |
| 169 | response=response, |
| 170 | args=args, |
| 171 | kwargs=copy_kwargs, |
| 172 | ) |
| 173 | if isawaitable(cache_key): |
nothing calls this directly
no test coverage detected