concurrency.py 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. from __future__ import annotations
  2. import functools
  3. import sys
  4. import typing
  5. import warnings
  6. import anyio.to_thread
  7. if sys.version_info >= (3, 10): # pragma: no cover
  8. from typing import ParamSpec
  9. else: # pragma: no cover
  10. from typing_extensions import ParamSpec
  11. P = ParamSpec("P")
  12. T = typing.TypeVar("T")
  13. async def run_until_first_complete(*args: tuple[typing.Callable, dict]) -> None: # type: ignore[type-arg]
  14. warnings.warn(
  15. "run_until_first_complete is deprecated and will be removed in a future version.",
  16. DeprecationWarning,
  17. )
  18. async with anyio.create_task_group() as task_group:
  19. async def run(func: typing.Callable[[], typing.Coroutine]) -> None: # type: ignore[type-arg]
  20. await func()
  21. task_group.cancel_scope.cancel()
  22. for func, kwargs in args:
  23. task_group.start_soon(run, functools.partial(func, **kwargs))
  24. async def run_in_threadpool(func: typing.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
  25. if kwargs: # pragma: no cover
  26. # run_sync doesn't accept 'kwargs', so bind them in here
  27. func = functools.partial(func, **kwargs)
  28. return await anyio.to_thread.run_sync(func, *args)
  29. class _StopIteration(Exception):
  30. pass
  31. def _next(iterator: typing.Iterator[T]) -> T:
  32. # We can't raise `StopIteration` from within the threadpool iterator
  33. # and catch it outside that context, so we coerce them into a different
  34. # exception type.
  35. try:
  36. return next(iterator)
  37. except StopIteration:
  38. raise _StopIteration
  39. async def iterate_in_threadpool(
  40. iterator: typing.Iterable[T],
  41. ) -> typing.AsyncIterator[T]:
  42. as_iterator = iter(iterator)
  43. while True:
  44. try:
  45. yield await anyio.to_thread.run_sync(_next, as_iterator)
  46. except _StopIteration:
  47. break