Name | tubthumper JSON |
Version |
0.2.0
JSON |
| download |
home_page | |
Summary | Python package of retry utilities named after the English anarcho-communist rock band Chumbawamba's 1997 hit Tubthumping |
upload_time | 2024-01-09 04:24:10 |
maintainer | |
docs_url | None |
author | |
requires_python | >=3.8 |
license | Apache License, Version 2.0 |
keywords |
retry
jitter
exponential-backoff
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# **Tubthumper**: Helping you get up ... again!
[![CI/CD: n/a](https://github.com/matteosox/tubthumper/actions/workflows/cicd.yaml/badge.svg)](https://github.com/matteosox/tubthumper/actions/workflows/cicd.yaml)
[![Docs: n/a](https://readthedocs.org/projects/tubthumper/badge/?version=stable)](https://tubthumper.mattefay.com)
[![Downloads: n/a](https://static.pepy.tech/personalized-badge/tubthumper?period=total&units=none&left_color=grey&right_color=blue&left_text=Downloads)](https://pepy.tech/project/tubthumper)
[![PyPI: n/a](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=PyPI&query=%24.info.version&url=https%3A%2F%2Fpypi.org%2Fpypi%2Ftubthumper%2Fjson)](https://pypi.org/project/tubthumper/)
[![codecov: n/a](https://codecov.io/gh/matteosox/tubthumper/branch/main/graph/badge.svg?token=8VKKDG9SMZ)](https://codecov.io/gh/matteosox/tubthumper)
[![MyPY: n/a](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fmatteosox%2Fbd79bbd912687bf44fac6b7887d18f14%2Fraw%2Fmypy.json)](https://tubthumper.mattefay.com/en/stable/mypy.html)
[![Pylint: n/a](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fmatteosox%2Fbd79bbd912687bf44fac6b7887d18f14%2Fraw%2Fpylint.json)](https://tubthumper.mattefay.com/en/stable/pylint.html)
----
## What's in a name?
**Tubthumper** is a Python package of retry utilities named after the English anarcho-communist rock band Chumbawamba's 1997 hit [Tubthumping](https://www.youtube.com/watch?v=2H5uWRjFsGc). Yes, really.
> I get knocked down, but I get up again. 🎶\
> You're never gonna keep me down. 🎶\
> I get knocked down, but I get up again. 🎶\
> You're never gonna keep me down... 🎶
## Getting Started
### Installation
`tubthumper` is a pip-installable package [hosted on PyPI](https://pypi.org/project/tubthumper/). Getting started is as easy as:
```console
$ pip install tubthumper
```
`tubthumper` requires Python 3.8 or greater. For Python 3.10 or greater, it has no external dependencies, i.e. standard library only, but earlier versions require [`typing-extensions`](https://pypi.org/project/typing-extensions/).
### Usage
Import `tubthumper`'s useful bits:
```python
>>> from tubthumper import retry, retry_decorator, retry_factory
```
Call a function with retry and jittered exponential backoff:
```python
>>> retry(get_ip, exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 0.844422 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
Call that same function with positional and keyword arguments, e.g. retry `get_ip(42, "test", dev=True)`:
```python
>>> retry(get_ip,
... args=(42, "test"), kwargs={"dev": True},
... exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 0.420572 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
Bake retry behavior into your function with a decorator:
```python
>>> @retry_decorator(exceptions=ConnectionError)
... def get_ip_retry():
... return requests.get("http://ip.jsontest.com").json()
>>> get_ip_retry()
WARNING: Function threw exception below on try 1, retrying in 0.511275 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
Create a new function with retry behavior from an existing one:
```python
>>> get_ip_retry = retry_factory(get_ip, exceptions=ConnectionError)
>>> get_ip_retry()
WARNING: Function threw exception below on try 1, retrying in 0.783799 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
## Customization
While `tubthumper` ships with a set of sensible defaults, its retry behavior is fully customizable.
### Exceptions
Because overbroad except clauses are [the most diabolical Python antipattern](https://realpython.com/the-most-diabolical-python-antipattern/), there is no sensible default for what exception or exceptions to catch and retry. Thus, every `tubthumper` interface has a required `exceptions` keyword-only argument, which takes an exception or tuple of exceptions to catch and retry on, i.e. a sensible lack of a default.
```python
>>> retry(get_ip, exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 0.476597 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
>>> retry(get_ip, exceptions=(KeyError, ConnectionError))
WARNING: Function threw exception below on try 1, retrying in 0.908113 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
By default, `tubthumper` raises a `tubthumper.RetryError` exception when all retries have been exhausted:
```python
>>> retry(lambda: 1/0, retry_limit=0, exceptions=ZeroDivisionError)
Traceback (most recent call last):
...
tubthumper._retry_factory.RetryError: Retry limit 0 reached
```
You can override this behavior using the `reraise` flag to reraise the original exception in place of `RetryError`:
```python
>>> retry(lambda: 1/0, retry_limit=0, reraise=True, exceptions=ZeroDivisionError)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
```
### Retry Limits
By default, `tubthumper` will retry endlessly, but you have two means of limiting retry behavior. As shown previously, to limit the number of retries attempted, use the `retry_limit` keyword-only argument:
```python
>>> retry(lambda: 1/0, retry_limit=10, exceptions=ZeroDivisionError)
... # Warning logs for each failed call
Traceback (most recent call last):
...
tubthumper._retry_factory.RetryError: Retry limit 10 reached
```
Alternatively, you can use the `time_limit` keyword-only argument to prevent retry attempts after a certain duration:
```python
>>> retry(lambda: 1/0, time_limit=60, exceptions=ZeroDivisionError)
... # Warning logs for each failed call
Traceback (most recent call last):
...
tubthumper._retry_factory.RetryError: Time limit 60 exceeded
```
### Backoff timing
By default, the backoff duration doubles with each retry, starting off at one second. As well, each backoff period is jittered, i.e. scaled by a uniformly distributed random number on the [0.0, 1.0) interval. You can disable jittering using the `jitter` keyword-only argument:
```python
>>> retry(get_ip, jitter=False, exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 1 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
You can set the initial backoff duration using the `init_backoff` keyword-only argument:
```python
>>> retry(get_ip, jitter=False, init_backoff=10, exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 10 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
Finally, you can set the factor by which each successive backoff duration is scaled using the `exponential` keyword-only argument:
```python
>>> retry(get_ip, jitter=False, exponential=3, exceptions=ConnectionError)
WARNING: Function threw exception below on try 1, retrying in 1 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
WARNING: Function threw exception below on try 2, retrying in 3 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
### Logging
By default, `tubthumper` logs each caught exception at the `logging.WARNING` level using a logger named `tubthumper`, i.e. `logging.getLogger("tubthumper")`. As described in the [Python logging tutorial](https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library), for this default logger, "events of severity WARNING and greater will be printed to sys.stderr" if no further logging is configured.
You can set the logging level using the `log_level` keyword-only argument:
```python
>>> retry(get_ip, log_level=logging.DEBUG, exceptions=ConnectionError) # No warnings
{'ip': '8.8.8.8'}
```
You can provide your own logger using the `logger` keyword-only argument. This logger's `log` method will be called like so:
```python
logger.log(log_level, "Function threw...", exc_info=True)
```
## Features
### Compatible with methods
`tubthumper`'s various interfaces are compatible with methods, including classmethods and staticmethods:
```python
>>> class Class:
... @retry_decorator(exceptions=ConnectionError)
... def get_ip(self):
... return requests.get("http://ip.jsontest.com").json()
...
>>> Class().get_ip()
WARNING: Function threw exception below on try 1, retrying in 0.719705 seconds
Traceback (most recent call last):
...
requests.exceptions.ConnectionError: http://ip.jsontest.com
{'ip': '8.8.8.8'}
```
### Signature preserving
`tubthumper`'s various interfaces preserve the relevant [dunder](https://wiki.python.org/moin/DunderAlias) attributes of your function:
```python
>>> @retry_decorator(exceptions=ConnectionError)
... def func(one: bool, two: float = 3.0) -> complex:
... """This is a docstring"""
...
>>> func.__name__
'func'
>>> func.__qualname__
'func'
>>> func.__module__
'__main__'
>>> func.__doc__
'This is a docstring'
>>> func.__annotations__
{'one': <class 'bool'>, 'two': <class 'float'>, 'return': <class 'complex'>}
```
`tubthumper` also preserves the inspect module's function signature, and `is*` functions:
```python
>>> import inspect
>>> inspect.signature(func)
<Signature (one: bool, two: float = 3.0) -> complex>
>>> inspect.isfunction(func)
True
>>> inspect.isroutine(func)
True
>>> inspect.ismethod(Class().get_ip)
True
```
### Async support
`tubthumper`'s various interfaces support coroutine functions, including [generator-based coroutines](https://docs.python.org/3/library/asyncio-task.html#generator-based-coroutines), awaiting them while using `async.sleep` between awaits:
```python
>>> @retry_decorator(exceptions=ConnectionError)
... async def get_ip():
... return requests.get("http://ip.jsontest.com").json()
...
>>> inspect.iscoroutinefunction(get_ip)
True
```
### Fully type annotated
`tubthumper`'s various interfaces are fully type annotated, passing [Mypy](https://mypy.readthedocs.io/en/stable/)'s static type checker. You can find Mypy's [Type Check Coverage Summary](https://tubthumper.mattefay.com/en/stable/mypy.html) at that link.
### 100% Test Coverage
`tubthumper` achieves 100% test coverage across three supported operating systems (Windows, MacOS, & Linux). You can find the [Linux coverage report](https://tubthumper.mattefay.com/en/stable/coverage.html) at that link, or the full coverage report on [Codecov](https://codecov.io/gh/matteosox/tubthumper).
Raw data
{
"_id": null,
"home_page": "",
"name": "tubthumper",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "retry,jitter,exponential-backoff",
"author": "",
"author_email": "Matt Fay <matt.e.fay@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/b2/0b/c0ed27c0526860660d9c3e4fb7d3aa3620ac564e1de037fd80248817255c/tubthumper-0.2.0.tar.gz",
"platform": null,
"description": "# **Tubthumper**: Helping you get up ... again!\n\n[![CI/CD: n/a](https://github.com/matteosox/tubthumper/actions/workflows/cicd.yaml/badge.svg)](https://github.com/matteosox/tubthumper/actions/workflows/cicd.yaml)\n[![Docs: n/a](https://readthedocs.org/projects/tubthumper/badge/?version=stable)](https://tubthumper.mattefay.com)\n[![Downloads: n/a](https://static.pepy.tech/personalized-badge/tubthumper?period=total&units=none&left_color=grey&right_color=blue&left_text=Downloads)](https://pepy.tech/project/tubthumper)\n[![PyPI: n/a](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=PyPI&query=%24.info.version&url=https%3A%2F%2Fpypi.org%2Fpypi%2Ftubthumper%2Fjson)](https://pypi.org/project/tubthumper/)\n[![codecov: n/a](https://codecov.io/gh/matteosox/tubthumper/branch/main/graph/badge.svg?token=8VKKDG9SMZ)](https://codecov.io/gh/matteosox/tubthumper)\n[![MyPY: n/a](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fmatteosox%2Fbd79bbd912687bf44fac6b7887d18f14%2Fraw%2Fmypy.json)](https://tubthumper.mattefay.com/en/stable/mypy.html)\n[![Pylint: n/a](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Fmatteosox%2Fbd79bbd912687bf44fac6b7887d18f14%2Fraw%2Fpylint.json)](https://tubthumper.mattefay.com/en/stable/pylint.html)\n\n----\n\n## What's in a name?\n\n**Tubthumper** is a Python package of retry utilities named after the English anarcho-communist rock band Chumbawamba's 1997 hit [Tubthumping](https://www.youtube.com/watch?v=2H5uWRjFsGc). Yes, really.\n\n> I get knocked down, but I get up again. \ud83c\udfb6\\\n> You're never gonna keep me down. \ud83c\udfb6\\\n> I get knocked down, but I get up again. \ud83c\udfb6\\\n> You're never gonna keep me down... \ud83c\udfb6\n\n## Getting Started\n\n### Installation\n\n`tubthumper` is a pip-installable package [hosted on PyPI](https://pypi.org/project/tubthumper/). Getting started is as easy as:\n\n```console\n$ pip install tubthumper\n```\n\n`tubthumper` requires Python 3.8 or greater. For Python 3.10 or greater, it has no external dependencies, i.e. standard library only, but earlier versions require [`typing-extensions`](https://pypi.org/project/typing-extensions/).\n\n### Usage\n\nImport `tubthumper`'s useful bits:\n```python\n>>> from tubthumper import retry, retry_decorator, retry_factory\n```\n\nCall a function with retry and jittered exponential backoff:\n```python\n>>> retry(get_ip, exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 0.844422 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nCall that same function with positional and keyword arguments, e.g. retry `get_ip(42, \"test\", dev=True)`:\n```python\n>>> retry(get_ip,\n... args=(42, \"test\"), kwargs={\"dev\": True},\n... exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 0.420572 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nBake retry behavior into your function with a decorator:\n```python\n>>> @retry_decorator(exceptions=ConnectionError)\n... def get_ip_retry():\n... return requests.get(\"http://ip.jsontest.com\").json()\n>>> get_ip_retry()\nWARNING: Function threw exception below on try 1, retrying in 0.511275 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nCreate a new function with retry behavior from an existing one:\n```python\n>>> get_ip_retry = retry_factory(get_ip, exceptions=ConnectionError)\n>>> get_ip_retry()\nWARNING: Function threw exception below on try 1, retrying in 0.783799 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\n## Customization\n\nWhile `tubthumper` ships with a set of sensible defaults, its retry behavior is fully customizable.\n\n### Exceptions\n\nBecause overbroad except clauses are [the most diabolical Python antipattern](https://realpython.com/the-most-diabolical-python-antipattern/), there is no sensible default for what exception or exceptions to catch and retry. Thus, every `tubthumper` interface has a required `exceptions` keyword-only argument, which takes an exception or tuple of exceptions to catch and retry on, i.e. a sensible lack of a default.\n\n```python\n>>> retry(get_ip, exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 0.476597 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n>>> retry(get_ip, exceptions=(KeyError, ConnectionError))\nWARNING: Function threw exception below on try 1, retrying in 0.908113 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nBy default, `tubthumper` raises a `tubthumper.RetryError` exception when all retries have been exhausted:\n\n```python\n>>> retry(lambda: 1/0, retry_limit=0, exceptions=ZeroDivisionError)\nTraceback (most recent call last):\n ...\ntubthumper._retry_factory.RetryError: Retry limit 0 reached\n```\n\nYou can override this behavior using the `reraise` flag to reraise the original exception in place of `RetryError`:\n\n```python\n>>> retry(lambda: 1/0, retry_limit=0, reraise=True, exceptions=ZeroDivisionError)\nTraceback (most recent call last):\n ...\nZeroDivisionError: division by zero\n```\n\n### Retry Limits\n\nBy default, `tubthumper` will retry endlessly, but you have two means of limiting retry behavior. As shown previously, to limit the number of retries attempted, use the `retry_limit` keyword-only argument:\n\n```python\n>>> retry(lambda: 1/0, retry_limit=10, exceptions=ZeroDivisionError)\n... # Warning logs for each failed call\nTraceback (most recent call last):\n ...\ntubthumper._retry_factory.RetryError: Retry limit 10 reached\n```\n\nAlternatively, you can use the `time_limit` keyword-only argument to prevent retry attempts after a certain duration:\n\n```python\n>>> retry(lambda: 1/0, time_limit=60, exceptions=ZeroDivisionError)\n... # Warning logs for each failed call\nTraceback (most recent call last):\n ...\ntubthumper._retry_factory.RetryError: Time limit 60 exceeded\n```\n\n### Backoff timing\n\nBy default, the backoff duration doubles with each retry, starting off at one second. As well, each backoff period is jittered, i.e. scaled by a uniformly distributed random number on the [0.0, 1.0) interval. You can disable jittering using the `jitter` keyword-only argument:\n\n```python\n>>> retry(get_ip, jitter=False, exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 1 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nYou can set the initial backoff duration using the `init_backoff` keyword-only argument:\n\n```python\n>>> retry(get_ip, jitter=False, init_backoff=10, exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 10 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\nFinally, you can set the factor by which each successive backoff duration is scaled using the `exponential` keyword-only argument:\n\n```python\n>>> retry(get_ip, jitter=False, exponential=3, exceptions=ConnectionError)\nWARNING: Function threw exception below on try 1, retrying in 1 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\nWARNING: Function threw exception below on try 2, retrying in 3 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\n### Logging\n\nBy default, `tubthumper` logs each caught exception at the `logging.WARNING` level using a logger named `tubthumper`, i.e. `logging.getLogger(\"tubthumper\")`. As described in the [Python logging tutorial](https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library), for this default logger, \"events of severity WARNING and greater will be printed to sys.stderr\" if no further logging is configured.\n\nYou can set the logging level using the `log_level` keyword-only argument:\n\n```python\n>>> retry(get_ip, log_level=logging.DEBUG, exceptions=ConnectionError) # No warnings\n{'ip': '8.8.8.8'}\n```\n\nYou can provide your own logger using the `logger` keyword-only argument. This logger's `log` method will be called like so:\n\n```python\nlogger.log(log_level, \"Function threw...\", exc_info=True)\n```\n\n## Features\n\n### Compatible with methods\n\n`tubthumper`'s various interfaces are compatible with methods, including classmethods and staticmethods:\n\n```python\n>>> class Class:\n... @retry_decorator(exceptions=ConnectionError)\n... def get_ip(self):\n... return requests.get(\"http://ip.jsontest.com\").json()\n...\n>>> Class().get_ip()\nWARNING: Function threw exception below on try 1, retrying in 0.719705 seconds\nTraceback (most recent call last):\n ...\nrequests.exceptions.ConnectionError: http://ip.jsontest.com\n{'ip': '8.8.8.8'}\n```\n\n### Signature preserving\n\n`tubthumper`'s various interfaces preserve the relevant [dunder](https://wiki.python.org/moin/DunderAlias) attributes of your function:\n\n```python\n>>> @retry_decorator(exceptions=ConnectionError)\n... def func(one: bool, two: float = 3.0) -> complex:\n... \"\"\"This is a docstring\"\"\"\n...\n>>> func.__name__\n'func'\n>>> func.__qualname__\n'func'\n>>> func.__module__\n'__main__'\n>>> func.__doc__\n'This is a docstring'\n>>> func.__annotations__\n{'one': <class 'bool'>, 'two': <class 'float'>, 'return': <class 'complex'>}\n```\n\n`tubthumper` also preserves the inspect module's function signature, and `is*` functions:\n\n```python\n>>> import inspect\n>>> inspect.signature(func)\n<Signature (one: bool, two: float = 3.0) -> complex>\n>>> inspect.isfunction(func)\nTrue\n>>> inspect.isroutine(func)\nTrue\n>>> inspect.ismethod(Class().get_ip)\nTrue\n```\n\n### Async support\n\n`tubthumper`'s various interfaces support coroutine functions, including [generator-based coroutines](https://docs.python.org/3/library/asyncio-task.html#generator-based-coroutines), awaiting them while using `async.sleep` between awaits:\n\n```python\n>>> @retry_decorator(exceptions=ConnectionError)\n... async def get_ip():\n... return requests.get(\"http://ip.jsontest.com\").json()\n...\n>>> inspect.iscoroutinefunction(get_ip)\nTrue\n```\n\n### Fully type annotated\n\n`tubthumper`'s various interfaces are fully type annotated, passing [Mypy](https://mypy.readthedocs.io/en/stable/)'s static type checker. You can find Mypy's [Type Check Coverage Summary](https://tubthumper.mattefay.com/en/stable/mypy.html) at that link.\n\n### 100% Test Coverage\n\n`tubthumper` achieves 100% test coverage across three supported operating systems (Windows, MacOS, & Linux). You can find the [Linux coverage report](https://tubthumper.mattefay.com/en/stable/coverage.html) at that link, or the full coverage report on [Codecov](https://codecov.io/gh/matteosox/tubthumper).\n",
"bugtrack_url": null,
"license": "Apache License, Version 2.0",
"summary": "Python package of retry utilities named after the English anarcho-communist rock band Chumbawamba's 1997 hit Tubthumping",
"version": "0.2.0",
"project_urls": {
"Bug Tracker": "https://github.com/matteosox/tubthumper/issues",
"Changelog": "https://tubthumper.mattefay.com/en/stable/changelog.html",
"Documentation": "https://tubthumper.mattefay.com",
"Source": "https://github.com/matteosox/tubthumper"
},
"split_keywords": [
"retry",
"jitter",
"exponential-backoff"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "11e05e8dff8bf7e9a0e688cc1214bb2a8a14c857308eca5a59604c756e58ce39",
"md5": "0309e47e9605084fbe1045bf6fd52705",
"sha256": "f49d23c2621e7890af27452f59c8ea9b5c8748fb544ffe2d56f6e2c812e8126a"
},
"downloads": -1,
"filename": "tubthumper-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0309e47e9605084fbe1045bf6fd52705",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 13378,
"upload_time": "2024-01-09T04:24:08",
"upload_time_iso_8601": "2024-01-09T04:24:08.502974Z",
"url": "https://files.pythonhosted.org/packages/11/e0/5e8dff8bf7e9a0e688cc1214bb2a8a14c857308eca5a59604c756e58ce39/tubthumper-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b20bc0ed27c0526860660d9c3e4fb7d3aa3620ac564e1de037fd80248817255c",
"md5": "ec9d91906d948f4e17a976f0b368612f",
"sha256": "22397cd4babe15bec5cb6adb402674da433618f2689b797525fc9493bbe66464"
},
"downloads": -1,
"filename": "tubthumper-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "ec9d91906d948f4e17a976f0b368612f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 16211,
"upload_time": "2024-01-09T04:24:10",
"upload_time_iso_8601": "2024-01-09T04:24:10.204828Z",
"url": "https://files.pythonhosted.org/packages/b2/0b/c0ed27c0526860660d9c3e4fb7d3aa3620ac564e1de037fd80248817255c/tubthumper-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-01-09 04:24:10",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "matteosox",
"github_project": "tubthumper",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "tubthumper"
}