:param coroFuncs: iterable of no-arg functions
    :param totalTimeout:
    :param retryWait:
    :param acceptableExceptions:
    :param acceptableFails: how many of the passed in coroutines can
            ultimately fail and still be ok
    :return:

async def eventuallyAll(*coroFuncs: FlexFunc,  # (use functools.partials if needed)
                        totalTimeout: float,
                        retryWait: float=0.1,
                        acceptableExceptions=None,
                        acceptableFails: int=0,
                        override_timeout_limit=False):
    # TODO: Bug when `acceptableFails` > 0 if the first check fails, it will
    # exhaust the entire timeout.
    """
    :param coroFuncs: iterable of no-arg functions
    :param totalTimeout:
    :param retryWait:
    :param acceptableExceptions:
    :param acceptableFails: how many of the passed in coroutines can
            ultimately fail and still be ok
    :return:
    """
    start = time.perf_counter()

    def remaining():
        return totalTimeout + start - time.perf_counter()

    funcNames = []
    others = 0
    fails = 0
    rem = None
    for cf in coroFuncs:
        if len(funcNames) < 2:
            funcNames.append(get_func_name(cf))
        else:
            others += 1
        # noinspection PyBroadException
        try:
            rem = remaining()
            if rem <= 0:
                break
            await eventually(cf,
                             retryWait=retryWait,
                             timeout=rem,
                             acceptableExceptions=acceptableExceptions,
                             verbose=True,
                             override_timeout_limit=override_timeout_limit)
        except Exception as ex:
            if acceptableExceptions and type(ex) not in acceptableExceptions:
                raise
            fails += 1
            logger.debug("a coro {} with args {} timed out without succeeding; fail count: "
                         "{}, acceptable: {}".
                         format(get_func_name(cf), get_func_args(cf), fails, acceptableFails))
            if fails > acceptableFails:
                raise

    if rem is not None and rem <= 0:
        fails += 1
        if fails > acceptableFails:
            err = 'All checks could not complete successfully since total timeout ' \
                'expired {} sec ago'.format(-1 * rem if rem < 0 else 0)
            raise EventuallyTimeoutException(err)

    if others:
        funcNames.append("and {} others".format(others))
    desc = ", ".join(funcNames)
    logger.debug("{} succeeded with {:.2f} seconds to spare".
                 format(desc, remaining()))