Created
February 22, 2019 19:29
-
-
Save ultrafunkamsterdam/db2a0ff6d4ea189b893b9d24374f33e0 to your computer and use it in GitHub Desktop.
Python (Auto) Retry - Automatically retry your calls using optional delay - asyncio compatible
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import asyncio | |
import functools | |
import time | |
__all__ = ['Retry'] | |
class Retry(object): | |
""" | |
Python Retry - Automatically retry your calls using optional delay [detects blocking or non-blocking] | |
This wrapper/decorator will retry the wrapped function if: | |
- there is no return value | |
- an exception occured | |
""" | |
def __init__(self, retries=5, delay=2, debug=True, ignore_exc_types=()): | |
""" | |
Will retry the wrapped function if there is no return value or an exception is raised | |
If the decorated function is running inside an asyncio event loop, the delay is non-blocking | |
:param retries (int) : number of retries before giving up | |
:param delay (float) : delay in seconds between each retry attempt | |
:param debug (bool) : print retry attempts to console | |
:param ignore_exc_types (iterable) : iterable of exception classes that not trigger retrying | |
""" | |
self.retries = retries | |
self.delay = delay | |
self._debug = debug | |
self.ignore_exc_types = ignore_exc_types | |
try: | |
self._async = asyncio.get_running_loop() | |
except: | |
self._async = False | |
def __call__(self, fn): | |
if not self._async: | |
@functools.wraps(fn) | |
def decorated(*a, **k): | |
result = None | |
last_exception = None | |
for attempt in range(self.retries): | |
try: | |
result = fn(*a, **k) | |
if result: | |
return result | |
except self.ignore_exc_types as e: | |
last_exception = e | |
if self._debug: | |
print('{} retrying {} - in {} seconds - attempt {}/{}'.format( | |
self.__class__.__name__, fn.__name__, self.delay, attempt + 1, self.retries)) | |
time.sleep(self.delay) | |
if last_exception is not None: | |
raise type(last_exception) from last_exception | |
return result | |
return decorated | |
elif self._async: | |
@functools.wraps(fn) | |
async def decorated(*a, **k): | |
result = None | |
last_exception = None | |
for attempt in range(self.retries): | |
try: | |
result = fn(*a, **k) | |
if result: | |
return result | |
except self.ignore_exc_types as e: | |
last_exception = e | |
if self._debug: | |
print('{} retrying {} - in {} seconds - attempt {}/{}'.format( | |
self.__class__.__name__, fn.__name__, self.delay, attempt + 1, self.retries)) | |
await asyncio.sleep(self.delay) | |
if last_exception is not None: | |
raise type(last_exception) from last_exception | |
return result | |
return decorated |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment