Skip to content

Commit c09e569

Browse files
author
Doru Irimescu
committed
Modify to raise exception if retries unsuccessful, add decorator
1 parent 0ad60fd commit c09e569

File tree

2 files changed

+108
-29
lines changed

2 files changed

+108
-29
lines changed

src/exception_with_retry/source.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import functools
12
import time
3+
from typing import Any, Callable
24

35
__author__ = "Doru Irimescu"
46
__copyright__ = "Doru Irimescu"
@@ -27,7 +29,7 @@ def __init__(self, method, n_retry=5, sleep_time_s=0.5):
2729
self._max_retries = n_retry
2830
self._sleep_time_s = sleep_time_s
2931

30-
def run(self, args):
32+
def run(self, *args, **kwargs):
3133
"""Execute the wrapped method, and retry for _max_retries.
3234
3335
Args:
@@ -36,11 +38,38 @@ def run(self, args):
3638
Returns:
3739
[method return type]: value which the wrapped method returns.
3840
"""
39-
if self._retries < self._max_retries:
41+
42+
try:
4043
self._retries = self._retries + 1
41-
try:
42-
return self._method(*args)
43-
except Exception as e:
44+
return self._method(*args, **kwargs)
45+
except Exception as e:
46+
if self._retries < self._max_retries:
4447
print(e)
4548
time.sleep(self._sleep_time_s)
46-
return self.run(args)
49+
return self.run(*args, **kwargs)
50+
else:
51+
raise e
52+
53+
54+
def exception_with_retry(n_retry: int, sleep_time_s: float):
55+
"""Use this decorator to retry calling your function n_retry times,
56+
with sleep_time_s in between unsuccessful calls.
57+
Will finally throw the original error if the last retry fails.
58+
59+
Args:
60+
n_retry (int): number of retries
61+
sleep_time_s (float): time to sleep between unsuccessful retries, in seconds
62+
63+
Returns:
64+
Callable[..., Any]: decorated function
65+
"""
66+
67+
def dec(func: Callable[..., Any]) -> Callable[..., Any]:
68+
@functools.wraps(func)
69+
def wrapper(*args: Any, **kwargs: Any) -> Any:
70+
ewr = ExceptionWithRetry(func, n_retry, sleep_time_s)
71+
return ewr.run(*args, **kwargs)
72+
73+
return wrapper
74+
75+
return dec

tests/test_1.py

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
2+
from typing import List
23

3-
from exception_with_retry.source import ExceptionWithRetry
4+
from exception_with_retry.source import ExceptionWithRetry, exception_with_retry
45

56
__author__ = "Doru Irimescu"
67
__copyright__ = "Doru Irimescu"
@@ -31,39 +32,88 @@ def method_caller(arg):
3132
method(arg)
3233

3334

35+
def wrapper_test_method(n: int):
36+
if n < 0:
37+
raise Exception("Not gonna happen")
38+
else:
39+
return 15
40+
41+
3442
class TestExceptionWithRetry(unittest.TestCase):
3543
def test_1(self):
36-
ewr = ExceptionWithRetry(method, 5, 0.0)
37-
arg = [0]
38-
ewr.run([arg])
39-
self.assertEqual(arg, [0, 1, 2, 3, 4, 5])
44+
with self.assertRaises(Exception):
45+
ewr = ExceptionWithRetry(method, 5, 0.0)
46+
arg = [0]
47+
ewr.run(arg)
48+
self.assertEqual(arg, [0, 1, 2, 3, 4, 5])
4049

4150
def test_2(self):
42-
ewr = ExceptionWithRetry(method, 0, 0.0)
43-
arg = [0]
44-
ewr.run([arg])
45-
self.assertEqual(arg, [0])
51+
with self.assertRaises(Exception):
52+
ewr = ExceptionWithRetry(method, 0, 0.0)
53+
arg = [0]
54+
ewr.run(arg)
55+
self.assertEqual(arg, [0])
4656

4757
def test_3(self):
48-
ewr = ExceptionWithRetry(method2, 2, 0.0)
49-
arg = [0]
50-
ewr.run([arg, 1])
51-
self.assertEqual(arg, [0, 1, 2])
58+
with self.assertRaises(Exception):
59+
ewr = ExceptionWithRetry(method2, 2, 0.0)
60+
arg = [0]
61+
ewr.run(arg, 1)
62+
self.assertEqual(arg, [0, 1, 2])
5263

5364
def test_4(self):
54-
ewr = ExceptionWithRetry(method_caller, 2, 0.0)
55-
arg = [1, 2]
56-
ewr.run([arg])
57-
self.assertEqual(arg, [1, 2, 3, 4])
65+
with self.assertRaises(Exception):
66+
ewr = ExceptionWithRetry(method_caller, 2, 0.0)
67+
arg = [1, 2]
68+
ewr.run(arg)
69+
self.assertEqual(arg, [1, 2, 3, 4])
5870

5971
def test_5(self):
60-
t = TestMethods()
61-
ewr = ExceptionWithRetry(t.method, 5, 0.0)
62-
arg = [0]
63-
ewr.run([arg])
64-
self.assertEqual(arg, [0, 20, 40, 60, 80, 100])
72+
with self.assertRaises(Exception):
73+
t = TestMethods()
74+
ewr = ExceptionWithRetry(t.method, 5, 0.0)
75+
arg = [0]
76+
ewr.run(arg)
77+
self.assertEqual(arg, [0, 20, 40, 60, 80, 100])
6578

6679
def test_6(self):
6780
ewr = ExceptionWithRetry(method3, 5, 0.0)
68-
result = ewr.run([])
81+
result = ewr.run()
6982
self.assertEqual(result, 25)
83+
84+
85+
@exception_with_retry(3, 0.1)
86+
def wrapped_method(number_1: int, number_2: int = 0, calls: List = []):
87+
calls.append(1)
88+
if number_1 < 0:
89+
raise Exception("Not gonna happen")
90+
else:
91+
return number_1 + number_2
92+
93+
94+
class TestExceptionWithRetryDecorator(unittest.TestCase):
95+
def test_no_exception(self):
96+
result = wrapped_method(number_1=1)
97+
self.assertEqual(1, result)
98+
99+
calls = []
100+
result = wrapped_method(number_1=1, number_2=10, calls=calls)
101+
self.assertEqual(11, result)
102+
self.assertEqual(1, len(calls))
103+
104+
def test_throws_exception(self):
105+
with self.assertRaises(Exception):
106+
calls = []
107+
result = wrapped_method(-1, 0, calls)
108+
self.assertEqual(15, result)
109+
self.assertEqual(3, len(calls))
110+
111+
with self.assertRaises(Exception):
112+
calls = []
113+
wrapped_method(number_1=-1, number_2=2)
114+
self.assertEqual(3, len(calls))
115+
116+
with self.assertRaises(Exception):
117+
calls = []
118+
wrapped_method(-1, 2)
119+
self.assertEqual(3, len(calls))

0 commit comments

Comments
 (0)