|
from __future__ import annotations |
|
|
|
import functools |
|
import sys |
|
import typing |
|
import warnings |
|
|
|
import anyio.to_thread |
|
|
|
if sys.version_info >= (3, 10): |
|
from typing import ParamSpec |
|
else: |
|
from typing_extensions import ParamSpec |
|
|
|
P = ParamSpec("P") |
|
T = typing.TypeVar("T") |
|
|
|
|
|
async def run_until_first_complete(*args: tuple[typing.Callable, dict]) -> None: |
|
warnings.warn( |
|
"run_until_first_complete is deprecated and will be removed in a future version.", |
|
DeprecationWarning, |
|
) |
|
|
|
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[P, T], *args: P.args, **kwargs: P.kwargs) -> T: |
|
func = functools.partial(func, *args, **kwargs) |
|
return await anyio.to_thread.run_sync(func) |
|
|
|
|
|
class _StopIteration(Exception): |
|
pass |
|
|
|
|
|
def _next(iterator: typing.Iterator[T]) -> T: |
|
|
|
|
|
|
|
try: |
|
return next(iterator) |
|
except StopIteration: |
|
raise _StopIteration |
|
|
|
|
|
async def iterate_in_threadpool( |
|
iterator: typing.Iterable[T], |
|
) -> typing.AsyncIterator[T]: |
|
as_iterator = iter(iterator) |
|
while True: |
|
try: |
|
yield await anyio.to_thread.run_sync(_next, as_iterator) |
|
except _StopIteration: |
|
break |
|
|