|
- import functools
- import typing
- from typing import Any, AsyncGenerator, Iterator
-
- import anyio
-
- try:
- import contextvars # Python 3.7+ only or via contextvars backport.
- except ImportError: # pragma: no cover
- contextvars = None # type: ignore
-
-
- T = typing.TypeVar("T")
-
-
- async def run_until_first_complete(*args: typing.Tuple[typing.Callable, dict]) -> None:
- async with anyio.create_task_group() as task_group:
-
- async def run(func: typing.Callable[[], typing.Coroutine]) -> None:
- await func()
- task_group.cancel_scope.cancel()
-
- for func, kwargs in args:
- task_group.start_soon(run, functools.partial(func, **kwargs))
-
-
- async def run_in_threadpool(
- func: typing.Callable[..., T], *args: typing.Any, **kwargs: typing.Any
- ) -> T:
- if contextvars is not None: # pragma: no cover
- # Ensure we run in the same context
- child = functools.partial(func, *args, **kwargs)
- context = contextvars.copy_context()
- func = context.run
- args = (child,)
- elif kwargs: # pragma: no cover
- # run_sync doesn't accept 'kwargs', so bind them in here
- func = functools.partial(func, **kwargs)
- return await anyio.to_thread.run_sync(func, *args)
-
-
- class _StopIteration(Exception):
- pass
-
-
- def _next(iterator: Iterator) -> Any:
- # We can't raise `StopIteration` from within the threadpool iterator
- # and catch it outside that context, so we coerce them into a different
- # exception type.
- try:
- return next(iterator)
- except StopIteration:
- raise _StopIteration
-
-
- async def iterate_in_threadpool(iterator: Iterator) -> AsyncGenerator:
- while True:
- try:
- yield await anyio.to_thread.run_sync(_next, iterator)
- except _StopIteration:
- break
|