Run cached sync functions in thread pool just like FastAPI.
(*args: P.args, **kwargs: P.kwargs)
| 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] |