"""supporting functions"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/utils/execution.ipynb.

# %% auto 0
__all__ = ['auto_retry_sync', 'auto_retry_async', 'gather_with_concurrency']

# %% ../../nbs/utils/execution.ipynb 3
from typing import Callable, Any, List

from functools import wraps

import asyncio
import time

# %% ../../nbs/utils/execution.ipynb 5
def auto_retry_sync(
    max_retry=1, exceptions_to_ignore: List[Exception] = None, sleep_beween_retry=5
):

    exceptions_to_ignore = exceptions_to_ignore or []

    def actual_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):

            retry = 0

            while retry <= max_retry:
                try:
                    return func(*args, **kwargs)

                except Exception as e:
                    if any((isinstance(e, ig_ex) for ig_ex in exceptions_to_ignore)):
                        raise e

                    if retry >= max_retry:
                        raise e

                    retry += 1

                    print(f"retry decorator attempt - {retry}, {e}")
                    time.sleep(sleep_beween_retry)

        return wrapper

    return actual_decorator


def auto_retry_async(
    max_retry=1, exceptions_to_ignore: List[Exception] = None, sleep_beween_retry=5
):
    exceptions_to_ignore = exceptions_to_ignore or []

    def actual_decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):

            retry = 0

            while retry <= max_retry:
                try:
                    return await func(*args, **kwargs)

                except Exception as e:
                    if any((isinstance(e, ig_ex) for ig_ex in exceptions_to_ignore)):
                        raise e

                    if retry >= max_retry:
                        raise e

                    retry += 1

                    print(f"retry decorator attempt - {retry}, {e}")
                    await asyncio.sleep(sleep_beween_retry)

        return wrapper

    return actual_decorator

# %% ../../nbs/utils/execution.ipynb 10
async def gather_with_concurrency(
    *coros,  # list of coroutines to await
    n=60,  # number of open coroutines
):
    """limits the number of open coroutines at a time."""

    semaphore = asyncio.Semaphore(n)

    async def sem_coro(coro):
        async with semaphore:
            return await coro

    return await asyncio.gather(*(sem_coro(c) for c in coros))
