Opnieuw
=======
Opnieuw is a general-purpose retrying library, written in Python, in
order to simplify the task of adding retry behavior to both synchronous as well
as asynchronous tasks. Opnieuw is easy and straightforward to use.
Opnieuw makes it easy to add robust retries:
* There is a single retry strategy, exponential backoff with jitter, which
makes it impossible to forget to add jitter.
* It has just two parameters, to eliminate the possibility of invalid and
nonsensical combinations.
* Parameters are named clearly, to avoid confusing e.g. number of calls
(including the initial one) with number of retries (excluding the initial
call).
* The parameters are intuitive: instead of configuring the base delay for
exponential backoff, Opnieuw accepts a maximum number of calls to make, and
maximum time after the first call to still retry.
* Time units are clear from parameter names to make the decorated code obvious,
and readable without needing to refer to documentation.
See our [announcement post][post] for a bit more background on why we wrote
Opnieuw.
Example
-------
Suppose we want to parse `https://tech.channable.com/atom.xml` and we want to
add a retry to handle a specific network Exception. We can add Opnieuw to our
network handler as follows:
```python
import requests
from requests.exceptions import ConnectionError, HTTPError
from opnieuw import retry
@retry(
retry_on_exceptions=(ConnectionError, HTTPError),
max_calls_total=4,
retry_window_after_first_call_in_seconds=60,
)
def get_page() -> str:
response = requests.get('https://tech.channable.com/atom.xml')
return response.text
```
* `retry_on_exceptions` specifies which exceptions we want to retry on.
* `max_calls_total` is the maximum number of times that the decorated function
gets called, including the initial call.
* `retry_window_after_first_call_in_seconds` is the maximum number of seconds
after the first call was initiated, where we would still do a new attempt.
Features
--------
* Generic decorator API
* Specify retry exception (i.e. type of exception that we want retry)
* [Exponential backoff with jitter][exponential-backoff]
* Pre-shipped list of popular exceptions, which can easily be expanded
Installation
------------
To install Opnieuw, simply:
$ pip install opnieuw
More examples
--------
The short example above provides a concise demonstration of how Opnieuw could
be used. Let's dig deeper into Opnieuw and add another exception to
`retry_on_exceptions` to do a retry on:
```python
from urllib.error import URLError
import requests
from opnieuw import RetryException, retry
@retry(
retry_on_exceptions=(ConnectionError, RetryException, URLError),
max_calls_total=4,
retry_window_after_first_call_in_seconds=60,
)
def get_page() -> str:
response = requests.get('https://tech.channable.com/atom.xml')
if response.status_code != 200:
raise RetryException
return response.text
```
We can pass the name of exception we want do retry for or a tuple of exceptions
to the `retry_on_exceptions`. As mentioned earlier Opnieuw was developed to
make it more convenient to add retry behavior to any task.
Let's make it a little bit more generic and define a list of retry exceptions
that should trigger a retry of the function:
```python
STANDARD_HTTP_EXCEPTIONS = (
ConnectionError,
ProtocolError,
RetryException,
...
)
@retry(
retry_on_exceptions=STANDARD_HTTP_EXCEPTIONS,
max_calls_total=4,
retry_window_after_first_call_in_seconds=60,
)
def get_page() -> str:
response = requests.post('https://tech.channable.com/atom.xml')
return response.text
```
Now our retry is more generic, as exceptions raised which are in
`STANDARD_HTTP_EXCEPTIONS` will be retried.
If you want retry behavior for async tasks, then there is also an async retry
which basically work the same way, but for async tasks.
Here is the example above but in async mode:
```python
from opnieuw import retry_async
STANDARD_HTTP_EXCEPTIONS = (
ConnectionError,
EOFError,
RetryException,
...
)
@retry_async(
retry_on_exceptions=STANDARD_HTTP_EXCEPTIONS,
max_calls_total=4,
retry_window_after_first_call_in_seconds=60,
)
async def get_page() -> str:
response = requests.get('https://tech.channable.com/atom.xml')
return response.text
```
[post]: https://tech.channable.com/posts/2020-02-05-opnieuw.html
[exponential-backoff]: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
Raw data
{
"_id": null,
"home_page": "https://github.com/channable/opnieuw",
"name": "opnieuw",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "",
"author": "Channable",
"author_email": "ruud@channable.com",
"download_url": "https://files.pythonhosted.org/packages/b7/a5/b1c5860de17a426d33d18e4b56395716384ba59928f2d88e66d163497a2c/opnieuw-3.0.0.tar.gz",
"platform": null,
"description": "Opnieuw\n=======\n\nOpnieuw is a general-purpose retrying library, written in Python, in\norder to simplify the task of adding retry behavior to both synchronous as well\nas asynchronous tasks. Opnieuw is easy and straightforward to use.\n\nOpnieuw makes it easy to add robust retries:\n\n * There is a single retry strategy, exponential backoff with jitter, which\n makes it impossible to forget to add jitter.\n * It has just two parameters, to eliminate the possibility of invalid and\n nonsensical combinations.\n * Parameters are named clearly, to avoid confusing e.g. number of calls\n (including the initial one) with number of retries (excluding the initial\n call).\n * The parameters are intuitive: instead of configuring the base delay for\n exponential backoff, Opnieuw accepts a maximum number of calls to make, and\n maximum time after the first call to still retry.\n * Time units are clear from parameter names to make the decorated code obvious,\n and readable without needing to refer to documentation.\n\nSee our [announcement post][post] for a bit more background on why we wrote\nOpnieuw.\n\nExample\n-------\n\nSuppose we want to parse `https://tech.channable.com/atom.xml` and we want to\nadd a retry to handle a specific network Exception. We can add Opnieuw to our\nnetwork handler as follows:\n\n```python\nimport requests\nfrom requests.exceptions import ConnectionError, HTTPError\n\nfrom opnieuw import retry\n\n@retry(\n retry_on_exceptions=(ConnectionError, HTTPError),\n max_calls_total=4,\n retry_window_after_first_call_in_seconds=60,\n)\ndef get_page() -> str:\n response = requests.get('https://tech.channable.com/atom.xml')\n return response.text\n```\n\n * `retry_on_exceptions` specifies which exceptions we want to retry on.\n\n * `max_calls_total` is the maximum number of times that the decorated function\n gets called, including the initial call.\n\n * `retry_window_after_first_call_in_seconds` is the maximum number of seconds\n after the first call was initiated, where we would still do a new attempt.\n\nFeatures\n--------\n\n * Generic decorator API\n * Specify retry exception (i.e. type of exception that we want retry)\n * [Exponential backoff with jitter][exponential-backoff]\n * Pre-shipped list of popular exceptions, which can easily be expanded\n\nInstallation\n------------\n\nTo install Opnieuw, simply:\n\n $ pip install opnieuw\n\nMore examples\n--------\n\nThe short example above provides a concise demonstration of how Opnieuw could\nbe used. Let's dig deeper into Opnieuw and add another exception to\n`retry_on_exceptions` to do a retry on:\n\n```python\nfrom urllib.error import URLError\nimport requests\nfrom opnieuw import RetryException, retry\n\n@retry(\n retry_on_exceptions=(ConnectionError, RetryException, URLError),\n max_calls_total=4,\n retry_window_after_first_call_in_seconds=60,\n)\ndef get_page() -> str:\n response = requests.get('https://tech.channable.com/atom.xml')\n\n if response.status_code != 200:\n raise RetryException\n\n return response.text\n```\n\nWe can pass the name of exception we want do retry for or a tuple of exceptions\nto the `retry_on_exceptions`. As mentioned earlier Opnieuw was developed to\nmake it more convenient to add retry behavior to any task.\n\nLet's make it a little bit more generic and define a list of retry exceptions\nthat should trigger a retry of the function:\n\n```python\nSTANDARD_HTTP_EXCEPTIONS = (\n ConnectionError,\n ProtocolError,\n RetryException,\n ...\n)\n\n@retry(\n retry_on_exceptions=STANDARD_HTTP_EXCEPTIONS,\n max_calls_total=4,\n retry_window_after_first_call_in_seconds=60,\n)\ndef get_page() -> str:\n response = requests.post('https://tech.channable.com/atom.xml')\n return response.text\n```\n\nNow our retry is more generic, as exceptions raised which are in\n`STANDARD_HTTP_EXCEPTIONS` will be retried.\n\nIf you want retry behavior for async tasks, then there is also an async retry\nwhich basically work the same way, but for async tasks.\n\nHere is the example above but in async mode:\n\n```python\nfrom opnieuw import retry_async\n\nSTANDARD_HTTP_EXCEPTIONS = (\n ConnectionError,\n EOFError,\n RetryException,\n ...\n)\n\n@retry_async(\n retry_on_exceptions=STANDARD_HTTP_EXCEPTIONS,\n max_calls_total=4,\n retry_window_after_first_call_in_seconds=60,\n)\nasync def get_page() -> str:\n response = requests.get('https://tech.channable.com/atom.xml')\n return response.text\n```\n\n[post]: https://tech.channable.com/posts/2020-02-05-opnieuw.html\n[exponential-backoff]: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/\n",
"bugtrack_url": null,
"license": "",
"summary": "Retries for humans",
"version": "3.0.0",
"project_urls": {
"Homepage": "https://github.com/channable/opnieuw"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "f3f5389264f946a80663391eed9a9730103f20339616670edbc48f1001a6f623",
"md5": "0bc20993fdb89235b9bc7a182693d23b",
"sha256": "5b3d01831cc9afe8ceb5d5d67803450fd0c751105bdcbf215ec5efc21db2b239"
},
"downloads": -1,
"filename": "opnieuw-3.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0bc20993fdb89235b9bc7a182693d23b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 9355,
"upload_time": "2023-10-06T15:01:14",
"upload_time_iso_8601": "2023-10-06T15:01:14.528652Z",
"url": "https://files.pythonhosted.org/packages/f3/f5/389264f946a80663391eed9a9730103f20339616670edbc48f1001a6f623/opnieuw-3.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "b7a5b1c5860de17a426d33d18e4b56395716384ba59928f2d88e66d163497a2c",
"md5": "ad0ac0d2a4206e777bbfd7ff720da0cf",
"sha256": "071852cfe02c2fb954df3c3db482f647e3f192d6fc346dbf50a3ed103253f90a"
},
"downloads": -1,
"filename": "opnieuw-3.0.0.tar.gz",
"has_sig": false,
"md5_digest": "ad0ac0d2a4206e777bbfd7ff720da0cf",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 11548,
"upload_time": "2023-10-06T15:01:15",
"upload_time_iso_8601": "2023-10-06T15:01:15.911472Z",
"url": "https://files.pythonhosted.org/packages/b7/a5/b1c5860de17a426d33d18e4b56395716384ba59928f2d88e66d163497a2c/opnieuw-3.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-10-06 15:01:15",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "channable",
"github_project": "opnieuw",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "opnieuw"
}