aioretry


Nameaioretry JSON
Version 5.0.2 PyPI version JSON
download
home_pagehttps://github.com/kaelzhang/python-aioretry
SummaryAsyncio retry utility for Python 3.7+
upload_time2021-02-05 14:21:09
maintainer
docs_urlNone
authorKael Zhang
requires_python>=3.7
licenseMIT
keywords aioretry
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            [![](https://travis-ci.org/kaelzhang/python-aioretry.svg?branch=master)](https://travis-ci.org/kaelzhang/python-aioretry)
[![](https://codecov.io/gh/kaelzhang/python-aioretry/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/python-aioretry)
[![](https://img.shields.io/pypi/v/aioretry.svg)](https://pypi.org/project/aioretry/)
[![](https://img.shields.io/pypi/l/aioretry.svg)](https://github.com/kaelzhang/python-aioretry)

# aioretry

Asyncio retry utility for Python 3.7+

- [Upgrade guide](#upgrade-guide)

## Install

```sh
$ pip install aioretry
```

## Usage

```py
import asyncio
from typing import (
  Tuple
)

from aioretry import (
    retry,
    # Tuple[bool, Union[int, float]]
    RetryPolicyStrategy,
    RetryInfo
)

# This example shows the usage with python typings
def retry_policy(info: RetryInfo) -> RetryPolicyStrategy:
    """
    - It will always retry until succeeded
    - If fails for the first time, it will retry immediately,
    - If it fails again,
      aioretry will perform a 100ms delay before the second retry,
      200ms delay before the 3rd retry,
      the 4th retry immediately,
      100ms delay before the 5th retry,
      etc...
    """
    return False, (info.fails - 1) % 3 * 0.1


@retry(retry_policy)
async def connect_to_server():
    # connec to server
    ...

asyncio.run(connect_to_server())
```

### Use as class instance method decorator

We could also use `retry` as a decorator for instance method

```py
class Client:
    @retry(retry_policy)
    async def connect(self):
        await self._connect()

asyncio.run(Client().connect())
```

### Use instance method as retry policy

`retry_policy` could be the method name of the class if `retry` is used as a decorator for instance method.

```py
class ClientWithConfigurableRetryPolicy(Client):
    def __init__(self, max_retries: int = 3):
        self._max_retries = max_retries

    def _retry_policy(self, info: RetryInfo) -> RetryPolicyStrategy:
        return info.fails > self._max_retries, info.fails * 0.1

    # Then aioretry will use `self._retry_policy` as the retry policy.
    # And by using a str as the parameter `retry_policy`,
    # the decorator must be used for instance methods
    @retry('_retry_policy')
    async def connect(self):
        await self._connect()

asyncio.run(ClientWithConfigurableRetryPolicy(10).connect())
```

### Register an `before_retry` callback

We could also register an `before_retry` callback which will be executed after every failure of the target function if the corresponding retry is not abandoned.

```py
class ClientTrackableFailures(ClientWithConfigurableRetryPolicy):
    # `before_retry` could either be a sync function or an async function
    async def _before_retry(self, info: RetryInfo) -> None:
        await self._send_failure_log(info.exception, info.fails)

    @retry(
      retry_policy='_retry_policy',

      # Similar to `retry_policy`,
      # `before_retry` could either be a Callable or a str
      before_retry='_before_retry'
    )
    async def connect(self):
        await self._connect()
```

### Only retry for certain types of exceptions

```py
def retry_policy(info: RetryInfo) -> RetryPolicyStrategy:
    if isinstance(info.exception, (KeyError, ValueError)):
        # If it raises a KeyError or a ValueError, it will not retry.
        return True, 0

    # Otherwise, retry immediately
    return False, 0

@retry(retry_policy)
async def foo():
    # do something that might raise KeyError, ValueError or RuntimeError
    ...
```

## APIs

### retry(retry_policy, before_retry)(fn)

- **fn** `Callable[[...], Awaitable]` the function to be wrapped. The function should be an async function or normal function returns an awaitable.
- **retry_policy** `Union[str, RetryPolicy]`
- **before_retry?** `Optional[Union[str, Callable[[RetryInfo], Optional[Awaitable]]]]` If specified, `before_retry` is called after each failure of `fn` and before the corresponding retry. If the retry is abandoned, `before_retry` will not be executed.

Returns a wrapped function which accepts the same arguments as `fn` and returns an `Awaitable`.

### RetryPolicy

```py
RetryPolicy = Callable[[RetryInfo], Tuple[bool, Union[float, int]]]
```

Retry policy is used to determine what to do next after the `fn` fails to do some certain thing.

```py
abandon, delay = retry_policy(info)
```

- **info** `RetryInfo`
  - **info.fails** `int` is the counter number of how many times function `fn` performs as a failure. If `fn` fails for the first time, then `fails` will be `1`.
  - **info.exception** `Exception` is the exception that `fn` raised.
  - **info.since** `datetime` is the datetime when the first failure happens.
- If `abandon` is `True`, then aioretry will give up the retry and raise the exception directly, otherwise aioretry will sleep `delay` seconds (`asyncio.sleep(delay)`) before the next retry.

```py
def retry_policy(info: RetryInfo):
    if isinstance(info.exception, KeyError):
        # Just raise exceptions of type KeyError
        return True, 0

    return False, info.fails * 0.1
```

### Python typings

```py
from aioretry import (
    # The type of retry_policy function
    RetryPolicy,
    # The type of the return value of retry_policy function
    RetryPolicyStrategy,
    # The type of before_retry function
    BeforeRetry,
    RetryInfo
)
```

## Upgrade guide

Since `5.0.0`, aioretry introduces `RetryInfo` as the only parameter of `retry_policy` or `before_retry`

### 2.x -> 5.x

2.x

```py
def retry_policy(fails: int):
    """A policy that gives no chances to retry
    """

    return True, 0.1 * fails
```

5.x

```py
def retry_policy(info: RetryInfo):
    return True, 0.1 * info.fails
```

### 3.x -> 5.x

3.x

```py
def before_retry(e: Exception, fails: int):
    ...
```

5.x

```py
# Change the sequence of the parameters
def before_retry(info: RetryInfo):
    info.exception
    info.fails
    ...
```

### 4.x -> 5.x

Since `5.0.0`, both `retry_policy` and `before_retry` have only one parameter of type `RetryInfo` respectively.

## License

[MIT](LICENSE)



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/kaelzhang/python-aioretry",
    "name": "aioretry",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "aioretry",
    "author": "Kael Zhang",
    "author_email": "i+pypi@kael.me",
    "download_url": "https://files.pythonhosted.org/packages/3d/14/dab62af382fd39d72dd85a5fdacd80781ec2d9fde03b4ba37ada67f370c6/aioretry-5.0.2.tar.gz",
    "platform": "",
    "description": "[![](https://travis-ci.org/kaelzhang/python-aioretry.svg?branch=master)](https://travis-ci.org/kaelzhang/python-aioretry)\n[![](https://codecov.io/gh/kaelzhang/python-aioretry/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/python-aioretry)\n[![](https://img.shields.io/pypi/v/aioretry.svg)](https://pypi.org/project/aioretry/)\n[![](https://img.shields.io/pypi/l/aioretry.svg)](https://github.com/kaelzhang/python-aioretry)\n\n# aioretry\n\nAsyncio retry utility for Python 3.7+\n\n- [Upgrade guide](#upgrade-guide)\n\n## Install\n\n```sh\n$ pip install aioretry\n```\n\n## Usage\n\n```py\nimport asyncio\nfrom typing import (\n  Tuple\n)\n\nfrom aioretry import (\n    retry,\n    # Tuple[bool, Union[int, float]]\n    RetryPolicyStrategy,\n    RetryInfo\n)\n\n# This example shows the usage with python typings\ndef retry_policy(info: RetryInfo) -> RetryPolicyStrategy:\n    \"\"\"\n    - It will always retry until succeeded\n    - If fails for the first time, it will retry immediately,\n    - If it fails again,\n      aioretry will perform a 100ms delay before the second retry,\n      200ms delay before the 3rd retry,\n      the 4th retry immediately,\n      100ms delay before the 5th retry,\n      etc...\n    \"\"\"\n    return False, (info.fails - 1) % 3 * 0.1\n\n\n@retry(retry_policy)\nasync def connect_to_server():\n    # connec to server\n    ...\n\nasyncio.run(connect_to_server())\n```\n\n### Use as class instance method decorator\n\nWe could also use `retry` as a decorator for instance method\n\n```py\nclass Client:\n    @retry(retry_policy)\n    async def connect(self):\n        await self._connect()\n\nasyncio.run(Client().connect())\n```\n\n### Use instance method as retry policy\n\n`retry_policy` could be the method name of the class if `retry` is used as a decorator for instance method.\n\n```py\nclass ClientWithConfigurableRetryPolicy(Client):\n    def __init__(self, max_retries: int = 3):\n        self._max_retries = max_retries\n\n    def _retry_policy(self, info: RetryInfo) -> RetryPolicyStrategy:\n        return info.fails > self._max_retries, info.fails * 0.1\n\n    # Then aioretry will use `self._retry_policy` as the retry policy.\n    # And by using a str as the parameter `retry_policy`,\n    # the decorator must be used for instance methods\n    @retry('_retry_policy')\n    async def connect(self):\n        await self._connect()\n\nasyncio.run(ClientWithConfigurableRetryPolicy(10).connect())\n```\n\n### Register an `before_retry` callback\n\nWe could also register an `before_retry` callback which will be executed after every failure of the target function if the corresponding retry is not abandoned.\n\n```py\nclass ClientTrackableFailures(ClientWithConfigurableRetryPolicy):\n    # `before_retry` could either be a sync function or an async function\n    async def _before_retry(self, info: RetryInfo) -> None:\n        await self._send_failure_log(info.exception, info.fails)\n\n    @retry(\n      retry_policy='_retry_policy',\n\n      # Similar to `retry_policy`,\n      # `before_retry` could either be a Callable or a str\n      before_retry='_before_retry'\n    )\n    async def connect(self):\n        await self._connect()\n```\n\n### Only retry for certain types of exceptions\n\n```py\ndef retry_policy(info: RetryInfo) -> RetryPolicyStrategy:\n    if isinstance(info.exception, (KeyError, ValueError)):\n        # If it raises a KeyError or a ValueError, it will not retry.\n        return True, 0\n\n    # Otherwise, retry immediately\n    return False, 0\n\n@retry(retry_policy)\nasync def foo():\n    # do something that might raise KeyError, ValueError or RuntimeError\n    ...\n```\n\n## APIs\n\n### retry(retry_policy, before_retry)(fn)\n\n- **fn** `Callable[[...], Awaitable]` the function to be wrapped. The function should be an async function or normal function returns an awaitable.\n- **retry_policy** `Union[str, RetryPolicy]`\n- **before_retry?** `Optional[Union[str, Callable[[RetryInfo], Optional[Awaitable]]]]` If specified, `before_retry` is called after each failure of `fn` and before the corresponding retry. If the retry is abandoned, `before_retry` will not be executed.\n\nReturns a wrapped function which accepts the same arguments as `fn` and returns an `Awaitable`.\n\n### RetryPolicy\n\n```py\nRetryPolicy = Callable[[RetryInfo], Tuple[bool, Union[float, int]]]\n```\n\nRetry policy is used to determine what to do next after the `fn` fails to do some certain thing.\n\n```py\nabandon, delay = retry_policy(info)\n```\n\n- **info** `RetryInfo`\n  - **info.fails** `int` is the counter number of how many times function `fn` performs as a failure. If `fn` fails for the first time, then `fails` will be `1`.\n  - **info.exception** `Exception` is the exception that `fn` raised.\n  - **info.since** `datetime` is the datetime when the first failure happens.\n- If `abandon` is `True`, then aioretry will give up the retry and raise the exception directly, otherwise aioretry will sleep `delay` seconds (`asyncio.sleep(delay)`) before the next retry.\n\n```py\ndef retry_policy(info: RetryInfo):\n    if isinstance(info.exception, KeyError):\n        # Just raise exceptions of type KeyError\n        return True, 0\n\n    return False, info.fails * 0.1\n```\n\n### Python typings\n\n```py\nfrom aioretry import (\n    # The type of retry_policy function\n    RetryPolicy,\n    # The type of the return value of retry_policy function\n    RetryPolicyStrategy,\n    # The type of before_retry function\n    BeforeRetry,\n    RetryInfo\n)\n```\n\n## Upgrade guide\n\nSince `5.0.0`, aioretry introduces `RetryInfo` as the only parameter of `retry_policy` or `before_retry`\n\n### 2.x -> 5.x\n\n2.x\n\n```py\ndef retry_policy(fails: int):\n    \"\"\"A policy that gives no chances to retry\n    \"\"\"\n\n    return True, 0.1 * fails\n```\n\n5.x\n\n```py\ndef retry_policy(info: RetryInfo):\n    return True, 0.1 * info.fails\n```\n\n### 3.x -> 5.x\n\n3.x\n\n```py\ndef before_retry(e: Exception, fails: int):\n    ...\n```\n\n5.x\n\n```py\n# Change the sequence of the parameters\ndef before_retry(info: RetryInfo):\n    info.exception\n    info.fails\n    ...\n```\n\n### 4.x -> 5.x\n\nSince `5.0.0`, both `retry_policy` and `before_retry` have only one parameter of type `RetryInfo` respectively.\n\n## License\n\n[MIT](LICENSE)\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Asyncio retry utility for Python 3.7+",
    "version": "5.0.2",
    "project_urls": {
        "Homepage": "https://github.com/kaelzhang/python-aioretry"
    },
    "split_keywords": [
        "aioretry"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "4abdff157bf67376cf2085f9fb0554c965268b94b1d84b40633bfff92326739f",
                "md5": "c3ce3e8d1e2a858b287f3fd28e9a9ee8",
                "sha256": "ae0970f4079b91ef647fc342b4b24c3108c57c31ec64a9ec682daeb9936db302"
            },
            "downloads": -1,
            "filename": "aioretry-5.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c3ce3e8d1e2a858b287f3fd28e9a9ee8",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 5812,
            "upload_time": "2021-02-05T14:21:07",
            "upload_time_iso_8601": "2021-02-05T14:21:07.813687Z",
            "url": "https://files.pythonhosted.org/packages/4a/bd/ff157bf67376cf2085f9fb0554c965268b94b1d84b40633bfff92326739f/aioretry-5.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3d14dab62af382fd39d72dd85a5fdacd80781ec2d9fde03b4ba37ada67f370c6",
                "md5": "34c23a36491b45fdc6f773fbe222b455",
                "sha256": "fdfe11ab0a54b762e206f5485b2240e56c631f4dc6594f2d005af4c37e10511b"
            },
            "downloads": -1,
            "filename": "aioretry-5.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "34c23a36491b45fdc6f773fbe222b455",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 6437,
            "upload_time": "2021-02-05T14:21:09",
            "upload_time_iso_8601": "2021-02-05T14:21:09.638737Z",
            "url": "https://files.pythonhosted.org/packages/3d/14/dab62af382fd39d72dd85a5fdacd80781ec2d9fde03b4ba37ada67f370c6/aioretry-5.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-02-05 14:21:09",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "kaelzhang",
    "github_project": "python-aioretry",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": false,
    "requirements": [],
    "lcname": "aioretry"
}
        
Elapsed time: 0.11638s