riprova


Nameriprova JSON
Version 0.3.1 PyPI version JSON
download
home_pagehttps://github.com/h2non/riprova
SummarySmall and versatile library to retry failed operations using different backoff strategies
upload_time2023-08-07 09:23:46
maintainer
docs_urlNone
authorTomas Aparicio
requires_python
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            riprova |PyPI| |Coverage Status| |Documentation Status| |Quality| |Versions|
============================================================================

``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.

- `Constant backoff`_
- `Fibonacci backoff`_
- `Exponential backoff`_

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``):

.. code-block:: bash

    pip install -U riprova

Or install the latest sources from Github:

.. code-block:: bash

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


API
---

- riprova.retry_
- riprova.Retrier_
- riprova.AsyncRetrier_
- riprova.Backoff_
- riprova.ConstantBackoff_
- riprova.FibonacciBackoff_
- riprova.ExponentialBackoff_
- riprova.ErrorWhitelist_
- riprova.ErrorBlacklist_
- riprova.add_whitelist_error_
- riprova.RetryError_
- riprova.RetryTimeoutError_
- riprova.MaxRetriesExceeded_
- riprova.NotRetriableError_


.. _riprova.retry: http://riprova.readthedocs.io/en/latest/api.html#riprova.retry
.. _riprova.Retrier: http://riprova.readthedocs.io/en/latest/api.html#riprova.Retrier
.. _riprova.AsyncRetrier: http://riprova.readthedocs.io/en/latest/api.html#riprova.AsyncRetrier
.. _riprova.Backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.Backoff
.. _riprova.ConstantBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ConstantBackoff
.. _riprova.FibonacciBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.FibonacciBackoff
.. _riprova.ExponentialBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ExponentialBackoff
.. _riprova.ErrorWhitelist: http://riprova.readthedocs.io/en/latest/api.html#riprova.ErrorWhitelist
.. _riprova.ErrorBlacklist: http://riprova.readthedocs.io/en/latest/api.html#riprova.ErrorBlacklist
.. _riprova.add_whitelist_error: http://riprova.readthedocs.io/en/latest/api.html#riprova.add_whitelist_error
.. _riprova.RetryError: http://riprova.readthedocs.io/en/latest/api.html#riprova.RetryError
.. _riprova.RetryTimeoutError: http://riprova.readthedocs.io/en/latest/api.html#riprova.RetryTimeoutError
.. _riprova.MaxRetriesExceeded: http://riprova.readthedocs.io/en/latest/api.html#riprova.MaxRetriesExceeded
.. _riprova.NotRetriableError: http://riprova.readthedocs.io/en/latest/api.html#riprova.NotRetriableError


Examples
^^^^^^^^

You can see more featured examples from the `documentation` site.

**Basic usage examples**:

.. code-block:: python

    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**:

.. code-block:: python

    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

.. _exponential: https://en.wikipedia.org/wiki/Exponential_backoff
.. _fibonacci: https://en.wikipedia.org/wiki/Fibonacci_number
.. _asyncio: https://docs.python.org/3.5/library/asyncio.html
.. _Python: http://python.org
.. _annotated API reference: https://h2non.github.io/paco
.. _async/await: https://www.python.org/dev/peps/pep-0492/
.. _yield from: https://www.python.org/dev/peps/pep-0380/
.. _documentation: http://riprova.readthedocs.io/en/latest/examples.html
.. _read this article: http://dthain.blogspot.ie/2009/02/exponential-backoff-in-distributed.html
.. _Constant backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ConstantBackoff
.. _Fibonacci backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.FibonacciBackoff
.. _Exponential backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ExponentialBackOff
.. _ConstantBackoff: https://github.com/h2non/riprova/blob/master/riprova/strategies/constant.py
.. _whitelisting: https://github.com/h2non/riprova/blob/master/examples/whitelisting_errors.py
.. _blacklisting: https://github.com/h2non/riprova/blob/master/examples/blacklisting_errors.py
.. _error evaluation: https://github.com/h2non/riprova/blob/master/examples/http_request.py
.. _asynchronous coroutines: https://github.com/h2non/riprova/blob/master/examples/http_asyncio.py

.. |PyPI| image:: https://img.shields.io/pypi/v/riprova.svg?maxAge=2592000?style=flat-square
   :target: https://pypi.python.org/pypi/riprova
.. |Coverage Status| image:: https://coveralls.io/repos/github/h2non/riprova/badge.svg?branch=master
   :target: https://coveralls.io/github/h2non/riprova?branch=master
.. |Documentation Status| image:: https://img.shields.io/badge/docs-latest-green.svg?style=flat
   :target: http://riprova.readthedocs.io/en/latest/?badge=latest
.. |Quality| image:: https://codeclimate.com/github/h2non/riprova/badges/gpa.svg
   :target: https://codeclimate.com/github/h2non/riprova
.. |Stability| image:: https://img.shields.io/pypi/status/riprova.svg
   :target: https://pypi.python.org/pypi/riprova
.. |Versions| image:: https://img.shields.io/pypi/pyversions/riprova.svg
   :target: https://pypi.python.org/pypi/riprova



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


.. _#2: https://github.com/h2non/riprova/issues/2

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/h2non/riprova",
    "name": "riprova",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "",
    "author": "Tomas Aparicio",
    "author_email": "tomas+python@aparicio.me",
    "download_url": "https://files.pythonhosted.org/packages/2a/f7/b163b617abc6057221eec8e37daf437a6b81db5279bee368748be8e2e5b1/riprova-0.3.1.tar.gz",
    "platform": null,
    "description": "riprova |PyPI| |Coverage Status| |Documentation Status| |Quality| |Versions|\n============================================================================\n\n``riprova`` (meaning ``retry`` in Italian) is a small, general-purpose and versatile `Python`_ library\nthat provides retry mechanisms with multiple backoff strategies for any sort of failed operations.\n\nIt'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.\n\nFor a brief introduction about backoff mechanisms for potential failed operations, `read this article`_.\n\n\nFeatures\n--------\n\n-  Retry decorator for simple and idiomatic consumption.\n-  Simple Pythonic programmatic interface.\n-  Maximum retry timeout support.\n-  Supports error `whitelisting`_ and `blacklisting`_.\n-  Supports custom `error evaluation`_ retry logic (useful to retry only in specific cases).\n-  Automatically retry operations on raised exceptions.\n-  Supports `asynchronous coroutines`_ with both ``async/await`` and ``yield from`` syntax.\n-  Configurable maximum number of retry attempts.\n-  Highly configurable supporting max retries, timeouts or retry notifier callback.\n-  Built-in backoff strategies: constant, `fibonacci`_ and `exponential`_ backoffs.\n-  Supports sync/async context managers.\n-  Pluggable custom backoff strategies.\n-  Lightweight library with almost zero embedding cost.\n-  Works with Python +2.6, 3.0+ and PyPy.\n\n\nBackoff strategies\n------------------\n\nList of built-in backoff strategies.\n\n- `Constant backoff`_\n- `Fibonacci backoff`_\n- `Exponential backoff`_\n\nYou can also implement your own one easily.\nSee `ConstantBackoff`_ for an implementation reference.\n\n\nInstallation\n------------\n\nUsing ``pip`` package manager (requires pip 1.9+. Upgrade it running: ``pip install -U pip``):\n\n.. code-block:: bash\n\n    pip install -U riprova\n\nOr install the latest sources from Github:\n\n.. code-block:: bash\n\n    pip install -e git+git://github.com/h2non/riprova.git#egg=riprova\n\n\nAPI\n---\n\n- riprova.retry_\n- riprova.Retrier_\n- riprova.AsyncRetrier_\n- riprova.Backoff_\n- riprova.ConstantBackoff_\n- riprova.FibonacciBackoff_\n- riprova.ExponentialBackoff_\n- riprova.ErrorWhitelist_\n- riprova.ErrorBlacklist_\n- riprova.add_whitelist_error_\n- riprova.RetryError_\n- riprova.RetryTimeoutError_\n- riprova.MaxRetriesExceeded_\n- riprova.NotRetriableError_\n\n\n.. _riprova.retry: http://riprova.readthedocs.io/en/latest/api.html#riprova.retry\n.. _riprova.Retrier: http://riprova.readthedocs.io/en/latest/api.html#riprova.Retrier\n.. _riprova.AsyncRetrier: http://riprova.readthedocs.io/en/latest/api.html#riprova.AsyncRetrier\n.. _riprova.Backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.Backoff\n.. _riprova.ConstantBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ConstantBackoff\n.. _riprova.FibonacciBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.FibonacciBackoff\n.. _riprova.ExponentialBackoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ExponentialBackoff\n.. _riprova.ErrorWhitelist: http://riprova.readthedocs.io/en/latest/api.html#riprova.ErrorWhitelist\n.. _riprova.ErrorBlacklist: http://riprova.readthedocs.io/en/latest/api.html#riprova.ErrorBlacklist\n.. _riprova.add_whitelist_error: http://riprova.readthedocs.io/en/latest/api.html#riprova.add_whitelist_error\n.. _riprova.RetryError: http://riprova.readthedocs.io/en/latest/api.html#riprova.RetryError\n.. _riprova.RetryTimeoutError: http://riprova.readthedocs.io/en/latest/api.html#riprova.RetryTimeoutError\n.. _riprova.MaxRetriesExceeded: http://riprova.readthedocs.io/en/latest/api.html#riprova.MaxRetriesExceeded\n.. _riprova.NotRetriableError: http://riprova.readthedocs.io/en/latest/api.html#riprova.NotRetriableError\n\n\nExamples\n^^^^^^^^\n\nYou can see more featured examples from the `documentation` site.\n\n**Basic usage examples**:\n\n.. code-block:: python\n\n    import riprova\n\n    @riprova.retry\n    def task():\n        \"\"\"Retry operation if it fails with constant backoff (default)\"\"\"\n\n    @riprova.retry(backoff=riprova.ConstantBackoff(retries=5))\n    def task():\n        \"\"\"Retry operation if it fails with custom max number of retry attempts\"\"\"\n\n    @riprova.retry(backoff=riprova.ExponentialBackOff(factor=0.5))\n    def task():\n        \"\"\"Retry operation if it fails using exponential backoff\"\"\"\n\n    @riprova.retry(timeout=10)\n    def task():\n        \"\"\"Raises a TimeoutError if the retry loop exceeds from 10 seconds\"\"\"\n\n    def on_retry(err, next_try):\n        print('Operation error: {}'.format(err))\n        print('Next try in: {}ms'.format(next_try))\n\n    @riprova.retry(on_retry=on_retry)\n    def task():\n        \"\"\"Subscribe via function callback to every retry attempt\"\"\"\n\n    def evaluator(response):\n        # Force retry operation if not a valid response\n        if response.status >= 400:\n            raise RuntimeError('invalid response status')  # or simple return True\n        # Otherwise return False, meaning no retry\n        return False\n\n    @riprova.retry(evaluator=evaluator)\n    def task():\n        \"\"\"Use a custom evaluator function to determine if the operation failed or not\"\"\"\n\n    @riprova.retry\n    async def task():\n        \"\"\"Asynchronous coroutines are also supported :)\"\"\"\n\n\n**Retry failed HTTP requests**:\n\n.. code-block:: python\n\n    import pook\n    import requests\n    from riprova import retry\n\n    # Define HTTP mocks to simulate failed requests\n    pook.get('server.com').times(3).reply(503)\n    pook.get('server.com').times(1).reply(200).json({'hello': 'world'})\n\n\n    # Retry evaluator function used to determine if the operated failed or not\n    def evaluator(response):\n        if response != 200:\n            return Exception('failed request')  # you can also simply return True\n        return False\n\n\n    # On retry even subscriptor\n    def on_retry(err, next_try):\n        print('Operation error {}'.format(err))\n        print('Next try in {}ms'.format(next_try))\n\n\n    # Register retriable operation\n    @retry(evaluator=evaluator, on_retry=on_retry)\n    def fetch(url):\n        return requests.get(url)\n\n\n    # Run task that might fail\n    fetch('http://server.com')\n\n\n\nLicense\n-------\n\nMIT - Tomas Aparicio\n\n.. _exponential: https://en.wikipedia.org/wiki/Exponential_backoff\n.. _fibonacci: https://en.wikipedia.org/wiki/Fibonacci_number\n.. _asyncio: https://docs.python.org/3.5/library/asyncio.html\n.. _Python: http://python.org\n.. _annotated API reference: https://h2non.github.io/paco\n.. _async/await: https://www.python.org/dev/peps/pep-0492/\n.. _yield from: https://www.python.org/dev/peps/pep-0380/\n.. _documentation: http://riprova.readthedocs.io/en/latest/examples.html\n.. _read this article: http://dthain.blogspot.ie/2009/02/exponential-backoff-in-distributed.html\n.. _Constant backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ConstantBackoff\n.. _Fibonacci backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.FibonacciBackoff\n.. _Exponential backoff: http://riprova.readthedocs.io/en/latest/api.html#riprova.ExponentialBackOff\n.. _ConstantBackoff: https://github.com/h2non/riprova/blob/master/riprova/strategies/constant.py\n.. _whitelisting: https://github.com/h2non/riprova/blob/master/examples/whitelisting_errors.py\n.. _blacklisting: https://github.com/h2non/riprova/blob/master/examples/blacklisting_errors.py\n.. _error evaluation: https://github.com/h2non/riprova/blob/master/examples/http_request.py\n.. _asynchronous coroutines: https://github.com/h2non/riprova/blob/master/examples/http_asyncio.py\n\n.. |PyPI| image:: https://img.shields.io/pypi/v/riprova.svg?maxAge=2592000?style=flat-square\n   :target: https://pypi.python.org/pypi/riprova\n.. |Coverage Status| image:: https://coveralls.io/repos/github/h2non/riprova/badge.svg?branch=master\n   :target: https://coveralls.io/github/h2non/riprova?branch=master\n.. |Documentation Status| image:: https://img.shields.io/badge/docs-latest-green.svg?style=flat\n   :target: http://riprova.readthedocs.io/en/latest/?badge=latest\n.. |Quality| image:: https://codeclimate.com/github/h2non/riprova/badges/gpa.svg\n   :target: https://codeclimate.com/github/h2non/riprova\n.. |Stability| image:: https://img.shields.io/pypi/status/riprova.svg\n   :target: https://pypi.python.org/pypi/riprova\n.. |Versions| image:: https://img.shields.io/pypi/pyversions/riprova.svg\n   :target: https://pypi.python.org/pypi/riprova\n\n\n\nv0.3.0 / 2023-05-20\n-------------------\n\n  * Deprecate asyncio.corouting\n  * Drop Python 2 and 3.4 support\n\nv0.2.7 / 2018-08-24\n-------------------\n\n  * Merge pull request #20 from ffix/forward-exception-instance\n  * Correct linter warnings\n  * Re-raise exception instance instead of new exception with no args\n  * Merge pull request #19 from EdwardBetts/spelling\n  * Correct spelling mistakes.\n  * feat(setup): support Python 3.7\n  * feat(History): add version changes\n\nv0.2.6 / 2018-04-14\n-------------------\n\n* fix(#17): handle as legit retriable error Timeout exceptions.\n\nv0.2.5 / 2018-03-21\n-------------------\n\n* Merge pull request #15 from jstasiak/allow-newer-six\n* Allow newer six\n* feat(History): update changes\n\nv0.2.5 / 2018-03-21\n-------------------\n\n* Merge pull request #15 from jstasiak/allow-newer-six\n* Allow newer six\n* feat(History): update changes\n\nv0.2.4 / 2018-03-20\n-------------------\n\n* merge(#14): Allow subsecond maxtimes for ExponentialBackoff\n\nv0.2.3 / 2017-01-13\n-------------------\n\n* refactor(retry): remove unnecessary partial function\n* fix(retry): rename keyword param for partial application\n* feat(docs): improve description\n* refactor(Makefile): update publish task\n\nv0.2.2 / 2017-01-06\n-------------------\n\n* feat(package): add wheel distribution\n\nv0.2.1 / 2017-01-04\n-------------------\n\n* fix(retrier): remove debug print statement\n\nv0.2.0 / 2017-01-02\n-------------------\n\n* feat(core): use seconds as default time unit (introduces API breaking changes)\n* refactor(examples): update examples to use new time unit\n* feat(contextmanager): adds context manager support\n* feat(examples): add context manager example\n* feat: add context managers support\n\nv0.1.3 / 2016-12-30\n-------------------\n\n* refactor(async_retrier): simplify coroutine wrapper\n* feat(whitelist): add whitelist and blacklist support\n* feat(tests): add missing test cases for whitelist\n* feat(retry): pass error_evaluator param\n* fix(retrier): cast delay to float\n* fix(tests): use valid exception for Python 2.7\n* feat(#6): add custom error whilelist and custom error evaluator function\n* Merge pull request #8 from tsarpaul/master\n* refactor(decorator): do not expose retrier instance\n\nv0.1.2 / 2016-12-27\n-------------------\n\n* fix(decorator): wrap retries instance per function call\n\nv0.1.1 / 2016-12-27\n-------------------\n\n* fix(`#2`_): handle and forward ``asyncio.CancelledError`` as non-retriable error\n\nv0.1.0 / 2016-12-25\n-------------------\n\n* First version\n\n\n.. _#2: https://github.com/h2non/riprova/issues/2\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Small and versatile library to retry failed operations using different backoff strategies",
    "version": "0.3.1",
    "project_urls": {
        "Homepage": "https://github.com/h2non/riprova"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "176139b608b848bd1f4028f2fd3be1b1e38da625eecb248f19416af8fd2a7e20",
                "md5": "fa151734b279a0be8e9e0af1dcfaa187",
                "sha256": "fc1bd9bfd8befd453e322b2111839ecb3a0ccabb8af6ff10c5942f1a2143f807"
            },
            "downloads": -1,
            "filename": "riprova-0.3.1-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "fa151734b279a0be8e9e0af1dcfaa187",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 21931,
            "upload_time": "2023-08-07T09:23:43",
            "upload_time_iso_8601": "2023-08-07T09:23:43.983199Z",
            "url": "https://files.pythonhosted.org/packages/17/61/39b608b848bd1f4028f2fd3be1b1e38da625eecb248f19416af8fd2a7e20/riprova-0.3.1-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2af7b163b617abc6057221eec8e37daf437a6b81db5279bee368748be8e2e5b1",
                "md5": "d0bc0b180bd04f93b61f23244cb043d9",
                "sha256": "16017249bbc18dc654d9b8e8e34ff53bb82573aa05596d398e29c439f3163152"
            },
            "downloads": -1,
            "filename": "riprova-0.3.1.tar.gz",
            "has_sig": false,
            "md5_digest": "d0bc0b180bd04f93b61f23244cb043d9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 20156,
            "upload_time": "2023-08-07T09:23:46",
            "upload_time_iso_8601": "2023-08-07T09:23:46.791822Z",
            "url": "https://files.pythonhosted.org/packages/2a/f7/b163b617abc6057221eec8e37daf437a6b81db5279bee368748be8e2e5b1/riprova-0.3.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-07 09:23:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "h2non",
    "github_project": "riprova",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "tox": true,
    "lcname": "riprova"
}
        
Elapsed time: 0.24322s