# coding:utf-8

import asyncio
import functools
import typing as t

import anyio

T = t.TypeVar("T")


async def run_in_threadpool(func, *args, **kwargs):
    if kwargs:
        func = functools.partial(func, **kwargs)
    return await anyio.to_thread.run_sync(func, *args)


class _StopIteration(Exception):
    pass


def _next(iterator: t.Iterator[T]) -> T:
    # 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: t.Iterator[T],
) -> t.AsyncIterator[T]:
    while True:
        try:
            yield await anyio.to_thread.run_sync(_next, iterator)
        except StopIteration:
            break


def is_async_callable(obj: t.Any) -> bool:
    while isinstance(obj, functools.partial):
        obj = obj.func

    return asyncio.iscoroutinefunction(obj) or (
            callable(obj) and asyncio.iscoroutinefunction(obj.__call__)
    )
