@optional_args
def try_multiple_dec(f, ntrials=None, duration=0.1, exceptions=None, increment_type=None, exceptions_filter=None, logger=None):
    "Decorator to try function multiple times.\n\n    Main purpose is to decorate functions dealing with removal of files/directories\n    and which might need a few seconds to work correctly on Windows which takes\n    its time to release files/directories.\n\n    Parameters\n    ----------\n    ntrials: int, optional\n    duration: float, optional\n      Seconds to sleep before retrying.\n    increment_type: {None, 'exponential'}\n      Note that if it is exponential, duration should typically be > 1.0\n      so it grows with higher power\n    exceptions: Exception or tuple of Exceptions, optional\n      Exception or a tuple of multiple exceptions, on which to retry\n    exceptions_filter: callable, optional\n      If provided, this function will be called with a caught exception\n      instance.  If function returns True - we will re-try, if False - exception\n      will be re-raised without retrying.\n    logger: callable, optional\n      Logger to log upon failure.  If not provided, will use stock logger\n      at the level of 5 (heavy debug).\n    "
    if (not exceptions):
        exceptions = ((OSError, WindowsError, PermissionError) if on_windows else OSError)
    if (not ntrials):
        ntrials = (100 if on_windows else 10)
    if (logger is None):

        def logger(*args, **kwargs):
            return lgr.log(5, *args, **kwargs)
    assert (increment_type in {None, 'exponential'})

    @wraps(f)
    def _wrap_try_multiple_dec(*args, **kwargs):
        t = duration
        for trial in range(ntrials):
            try:
                return f(*args, **kwargs)
            except exceptions as exc:
                if (exceptions_filter and (not exceptions_filter(exc))):
                    raise
                if (trial < (ntrials - 1)):
                    if (increment_type == 'exponential'):
                        t = (duration ** (trial + 1))
                    logger('Caught %s on trial #%d. Sleeping %f and retrying', CapturedException(exc), trial, t)
                    sleep(t)
                else:
                    raise
    return _wrap_try_multiple_dec
Decorator to try function multiple times.

Main purpose is to decorate functions dealing with removal of files/directories
and which might need a few seconds to work correctly on Windows which takes
its time to release files/directories.

Parameters
----------
ntrials: int, optional
duration: float, optional
  Seconds to sleep before retrying.
increment_type: {None, 'exponential'}
  Note that if it is exponential, duration should typically be > 1.0
  so it grows with higher power
exceptions: Exception or tuple of Exceptions, optional
  Exception or a tuple of multiple exceptions, on which to retry
exceptions_filter: callable, optional
  If provided, this function will be called with a caught exception
  instance.  If function returns True - we will re-try, if False - exception
  will be re-raised without retrying.
logger: callable, optional
  Logger to log upon failure.  If not provided, will use stock logger
  at the level of 5 (heavy debug).
@optional_args
def try_multiple_dec(f, ntrials=None, duration=0.1, exceptions=None, increment_type=None, exceptions_filter=None, logger=None):
    "Decorator to try function multiple times.\n\n    Main purpose is to decorate functions dealing with removal of files/directories\n    and which might need a few seconds to work correctly on Windows which takes\n    its time to release files/directories.\n\n    Parameters\n    ----------\n    ntrials: int, optional\n    duration: float, optional\n      Seconds to sleep before retrying.\n    increment_type: {None, 'exponential'}\n      Note that if it is exponential, duration should typically be > 1.0\n      so it grows with higher power\n    exceptions: Exception or tuple of Exceptions, optional\n      Exception or a tuple of multiple exceptions, on which to retry\n    exceptions_filter: callable, optional\n      If provided, this function will be called with a caught exception\n      instance.  If function returns True - we will re-try, if False - exception\n      will be re-raised without retrying.\n    logger: callable, optional\n      Logger to log upon failure.  If not provided, will use stock logger\n      at the level of 5 (heavy debug).\n    "
    if (not exceptions):
        exceptions = ((OSError, WindowsError, PermissionError) if on_windows else OSError)
    if (not ntrials):
        ntrials = (100 if on_windows else 10)
    if (logger is None):

        def logger(*args, **kwargs):
            return lgr.log(5, *args, **kwargs)
    assert (increment_type in {None, 'exponential'})

    @wraps(f)
    def _wrap_try_multiple_dec(*args, **kwargs):
        t = duration
        for trial in range(ntrials):
            try:
                return f(*args, **kwargs)
            except exceptions as exc:
                if (exceptions_filter and (not exceptions_filter(exc))):
                    raise
                if (trial < (ntrials - 1)):
                    if (increment_type == 'exponential'):
                        t = (duration ** (trial + 1))
                    logger('Caught %s on trial #%d. Sleeping %f and retrying', CapturedException(exc), trial, t)
                    sleep(t)
                else:
                    raise
    return _wrap_try_multiple_dec