Skip to content

Commit

Permalink
Add retry
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhammerl committed Sep 1, 2021
1 parent dd3f48c commit e8d8f0c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 1 deletion.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ You can similarly auto-capture exceptions using `resultify(...)`. Please note th
Err(TypeError())
```


You can `retry` a function that returns a `Result` type with a constant backoff.

```
>>> from resultify import resultify, retry
... @retry(retries=2, delay=2, initial_delay=1):
... @resultify(Exception)
... def foo():
... # do something that needs retrying here
```

This example waits 1 second before executing the initial call, then attempts the initial call, then executes two retries, spaces out two seconds from the previous call. If any execution was a success, the `Ok` value will be returned. If the retries were exhausted and no `Ok` was returned, we return the `Err` value.

Since documentation always lies, please refer to the unit tests for examples of usage.


Expand Down
23 changes: 23 additions & 0 deletions resultify/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from time import sleep
from functools import wraps
from typing import Any, Generic, TypeVar, Union, Type, Callable

Expand Down Expand Up @@ -113,3 +114,25 @@ def inner(*args, **kwargs):
return inner

return decorator


def retry(retries: int = 0, delay: int = 0, initial_delay: int = 0):
def decorator(
function: Callable[..., Union[Ok[T], Err[E]]]
) -> Callable[..., Union[Ok[T], Err[E]]]:
@wraps(function)
def func_with_retries(*args, **kwargs) -> Union[Ok[T], Err[E]]:
sleep(initial_delay)
_retries = retries
res: Union[Ok[T], Err[E]] = function(*args, **kwargs)
while _retries >= 1:
if res.is_ok():
break
sleep(delay)
_retries -= 1
res = function(*args, **kwargs)
return res

return func_with_retries

return decorator
49 changes: 48 additions & 1 deletion test/test_resultify.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from resultify import Err, Ok, UnwrapError, resultify
from resultify import Err, Ok, UnwrapError, resultify, retry


class TestOk:
Expand Down Expand Up @@ -124,3 +124,50 @@ def test_isinstance_result_type(self):
n = Err("nay")
assert isinstance(o, (Ok, Err))
assert isinstance(n, (Ok, Err))


class TestRetry:
def test_retry_fail(self):
class Config:
value = "asdfasdfasdf"
counter = 0
failing_tries = 999
retries = 2

config = Config()

@retry(retries=config.retries)
@resultify(Exception)
def foo():
config.counter += 1
if config.counter <= config.failing_tries:
raise TypeError()
else:
return config.value

x = foo()
assert isinstance(x, Err)
assert config.counter == 3

def test_retry_ok(self):
class Config:
value = "asdfasdfasdf"
counter = 0
failing_tries = 2
retries = 2

config = Config()

@retry(retries=config.retries)
@resultify(Exception)
def foo():
config.counter += 1
if config.counter <= config.failing_tries:
raise TypeError()
else:
return config.value

x = foo()
assert isinstance(x, Ok)
assert x.ok() == config.value
assert config.counter == 3

0 comments on commit e8d8f0c

Please sign in to comment.