Skip to main content

Small and versatile library to retry failed operations using different backoff strategies

Project description

riprova (meaning retry in Italian) is a small, general-purpose and versatile Python library that provides retry mechanisms with multiple backoff strategies for any sort of failed operations.

It’s domain agnostic, highly customizable, extensible and provides a minimal API that’s easy to instrument in any code base via decorators, context managers or raw API consumption.

For a brief introduction about backoff mechanisms for potential failed operations, read this article.

Features

  • Retry decorator for simple and idiomatic consumption.

  • Simple Pythonic programmatic interface.

  • Maximum retry timeout support.

  • Supports error whitelisting and blacklisting.

  • Supports custom error evaluation retry logic (useful to retry only in specific cases).

  • Automatically retry operations on raised exceptions.

  • Supports asynchronous coroutines with both async/await and yield from syntax.

  • Configurable maximum number of retry attempts.

  • Highly configurable supporting max retries, timeouts or retry notifier callback.

  • Built-in backoff strategies: constant, fibonacci and exponential backoffs.

  • Supports sync/async context managers.

  • Pluggable custom backoff strategies.

  • Lightweight library with almost zero embedding cost.

  • Works with Python +2.6, 3.0+ and PyPy.

Backoff strategies

List of built-in backoff strategies.

You can also implement your own one easily. See ConstantBackoff for an implementation reference.

Installation

Using pip package manager (requires pip 1.9+. Upgrade it running: pip install -U pip):

pip install -U riprova

Or install the latest sources from Github:

pip install -e git+git://github.com/h2non/riprova.git#egg=riprova

API

Examples

You can see more featured examples from the documentation site.

Basic usage examples:

import riprova

@riprova.retry
def task():
    """Retry operation if it fails with constant backoff (default)"""

@riprova.retry(backoff=riprova.ConstantBackoff(retries=5))
def task():
    """Retry operation if it fails with custom max number of retry attempts"""

@riprova.retry(backoff=riprova.ExponentialBackOff(factor=0.5))
def task():
    """Retry operation if it fails using exponential backoff"""

@riprova.retry(timeout=10)
def task():
    """Raises a TimeoutError if the retry loop exceeds from 10 seconds"""

def on_retry(err, next_try):
    print('Operation error: {}'.format(err))
    print('Next try in: {}ms'.format(next_try))

@riprova.retry(on_retry=on_retry)
def task():
    """Subscribe via function callback to every retry attempt"""

def evaluator(response):
    # Force retry operation if not a valid response
    if response.status >= 400:
        raise RuntimeError('invalid response status')  # or simple return True
    # Otherwise return False, meaning no retry
    return False

@riprova.retry(evaluator=evaluator)
def task():
    """Use a custom evaluator function to determine if the operation failed or not"""

@riprova.retry
async def task():
    """Asynchronous coroutines are also supported :)"""

Retry failed HTTP requests:

import pook
import requests
from riprova import retry

# Define HTTP mocks to simulate failed requests
pook.get('server.com').times(3).reply(503)
pook.get('server.com').times(1).reply(200).json({'hello': 'world'})


# Retry evaluator function used to determine if the operated failed or not
def evaluator(response):
    if response != 200:
        return Exception('failed request')  # you can also simply return True
    return False


# On retry even subscriptor
def on_retry(err, next_try):
    print('Operation error {}'.format(err))
    print('Next try in {}ms'.format(next_try))


# Register retriable operation
@retry(evaluator=evaluator, on_retry=on_retry)
def fetch(url):
    return requests.get(url)


# Run task that might fail
fetch('http://server.com')

License

MIT - Tomas Aparicio

v0.3.0 / 2023-05-20

  • Deprecate asyncio.corouting

  • Drop Python 2 and 3.4 support

v0.2.7 / 2018-08-24

  • Merge pull request #20 from ffix/forward-exception-instance

  • Correct linter warnings

  • Re-raise exception instance instead of new exception with no args

  • Merge pull request #19 from EdwardBetts/spelling

  • Correct spelling mistakes.

  • feat(setup): support Python 3.7

  • feat(History): add version changes

v0.2.6 / 2018-04-14

  • fix(#17): handle as legit retriable error Timeout exceptions.

v0.2.5 / 2018-03-21

  • Merge pull request #15 from jstasiak/allow-newer-six

  • Allow newer six

  • feat(History): update changes

v0.2.5 / 2018-03-21

  • Merge pull request #15 from jstasiak/allow-newer-six

  • Allow newer six

  • feat(History): update changes

v0.2.4 / 2018-03-20

  • merge(#14): Allow subsecond maxtimes for ExponentialBackoff

v0.2.3 / 2017-01-13

  • refactor(retry): remove unnecessary partial function

  • fix(retry): rename keyword param for partial application

  • feat(docs): improve description

  • refactor(Makefile): update publish task

v0.2.2 / 2017-01-06

  • feat(package): add wheel distribution

v0.2.1 / 2017-01-04

  • fix(retrier): remove debug print statement

v0.2.0 / 2017-01-02

  • feat(core): use seconds as default time unit (introduces API breaking changes)

  • refactor(examples): update examples to use new time unit

  • feat(contextmanager): adds context manager support

  • feat(examples): add context manager example

  • feat: add context managers support

v0.1.3 / 2016-12-30

  • refactor(async_retrier): simplify coroutine wrapper

  • feat(whitelist): add whitelist and blacklist support

  • feat(tests): add missing test cases for whitelist

  • feat(retry): pass error_evaluator param

  • fix(retrier): cast delay to float

  • fix(tests): use valid exception for Python 2.7

  • feat(#6): add custom error whilelist and custom error evaluator function

  • Merge pull request #8 from tsarpaul/master

  • refactor(decorator): do not expose retrier instance

v0.1.2 / 2016-12-27

  • fix(decorator): wrap retries instance per function call

v0.1.1 / 2016-12-27

  • fix(#2): handle and forward asyncio.CancelledError as non-retriable error

v0.1.0 / 2016-12-25

  • First version

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page