_sync.py 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. from __future__ import annotations
  2. import asyncio
  3. import functools
  4. from typing import TypeVar, Callable, Awaitable
  5. from typing_extensions import ParamSpec
  6. import anyio
  7. import sniffio
  8. import anyio.to_thread
  9. T_Retval = TypeVar("T_Retval")
  10. T_ParamSpec = ParamSpec("T_ParamSpec")
  11. async def to_thread(
  12. func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
  13. ) -> T_Retval:
  14. if sniffio.current_async_library() == "asyncio":
  15. return await asyncio.to_thread(func, *args, **kwargs)
  16. return await anyio.to_thread.run_sync(
  17. functools.partial(func, *args, **kwargs),
  18. )
  19. # inspired by `asyncer`, https://github.com/tiangolo/asyncer
  20. def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
  21. """
  22. Take a blocking function and create an async one that receives the same
  23. positional and keyword arguments.
  24. Usage:
  25. ```python
  26. def blocking_func(arg1, arg2, kwarg1=None):
  27. # blocking code
  28. return result
  29. result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1)
  30. ```
  31. ## Arguments
  32. `function`: a blocking regular callable (e.g. a function)
  33. ## Return
  34. An async function that takes the same positional and keyword arguments as the
  35. original one, that when called runs the same original function in a thread worker
  36. and returns the result.
  37. """
  38. async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval:
  39. return await to_thread(function, *args, **kwargs)
  40. return wrapper