Skip to content

Commit 093ac26

Browse files
authored
Add on_exception handler called with captured exception. (eSAMTrade#1)
Add on_exception handler called with captured exception by @warownia1
1 parent efcb326 commit 093ac26

File tree

4 files changed

+44
-8
lines changed

4 files changed

+44
-8
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGES
22
=======
33

4+
0.9.4
5+
-----
6+
7+
* Add on_exception parameter accepting a handler called when exception is captured.
8+
The callback can also be used to handle the exception and interrupt further retries.
9+
410
0.9.3
511
-----
612

README.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ retry decorator
4040

4141
.. code:: python
4242
43-
def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger):
43+
def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger,
44+
on_exception=None):
4445
"""Return a retry decorator.
4546
4647
:param exceptions: an exception or a tuple of exceptions to catch. default: Exception.
@@ -52,6 +53,9 @@ retry decorator
5253
fixed if a number, random if a range tuple (min, max)
5354
:param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
5455
default: retry.logging_logger. if None, logging is disabled.
56+
:param on_exception: handler called when exception occurs. will be passed the captured
57+
exception as an argument. further retries are stopped when handler
58+
returns True. default: None
5559
"""
5660
5761
Various retrying logic can be achieved by combination of arguments.
@@ -109,8 +113,7 @@ retry_call
109113
.. code:: python
110114
111115
def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1,
112-
jitter=0,
113-
logger=logging_logger):
116+
jitter=0, logger=logging_logger, on_exception=None):
114117
"""
115118
Calls a function and re-executes it if it failed.
116119
@@ -126,6 +129,9 @@ retry_call
126129
fixed if a number, random if a range tuple (min, max)
127130
:param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
128131
default: retry.logging_logger. if None, logging is disabled.
132+
:param on_exception: handler called when exception occurs. will be passed the captured
133+
exception as an argument. further retries are stopped when handler
134+
returns True. default: None
129135
:returns: the result of the f function.
130136
"""
131137

retry/api.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111

1212
def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0,
13-
logger=logging_logger, log_traceback=False):
13+
logger=logging_logger, log_traceback=False, on_exception=None):
1414
"""
1515
Executes a function and retries it if it failed.
1616
@@ -24,13 +24,20 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None,
2424
fixed if a number, random if a range tuple (min, max)
2525
:param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
2626
default: retry.logging_logger. if None, logging is disabled.
27+
:param on_exception: handler called when exception occurs. will be passed the captured
28+
exception as an argument. further retries are stopped when handler
29+
returns True. default: None
2730
:returns: the result of the f function.
2831
"""
2932
_tries, _delay = tries, delay
3033
while _tries:
3134
try:
3235
return f()
3336
except exceptions as e:
37+
if on_exception is not None:
38+
if on_exception(e):
39+
break
40+
3441
_tries -= 1
3542
if not _tries:
3643
raise
@@ -58,7 +65,7 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None,
5865

5966

6067
def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger,
61-
log_traceback=False):
68+
log_traceback=False, on_exception=None):
6269
"""Returns a retry decorator.
6370
6471
:param exceptions: an exception or a tuple of exceptions to catch. default: Exception.
@@ -70,6 +77,9 @@ def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, ji
7077
fixed if a number, random if a range tuple (min, max)
7178
:param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
7279
default: retry.logging_logger. if None, logging is disabled.
80+
:param on_exception: handler called when exception occurs. will be passed the captured
81+
exception as an argument. further retries are stopped when handler
82+
returns True. default: None
7383
:returns: a retry decorator.
7484
"""
7585

@@ -78,13 +88,13 @@ def retry_decorator(f, *fargs, **fkwargs):
7888
args = fargs if fargs else list()
7989
kwargs = fkwargs if fkwargs else dict()
8090
return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter,
81-
logger, log_traceback=log_traceback)
91+
logger, log_traceback, on_exception)
8292

8393
return retry_decorator
8494

8595

8696
def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1,
87-
jitter=0, logger=logging_logger, log_traceback=False):
97+
jitter=0, logger=logging_logger, log_traceback=False, on_exception=None):
8898
"""
8999
Calls a function and re-executes it if it failed.
90100
@@ -100,9 +110,12 @@ def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, dela
100110
fixed if a number, random if a range tuple (min, max)
101111
:param logger: logger.warning(fmt, error, delay) will be called on failed attempts.
102112
default: retry.logging_logger. if None, logging is disabled.
113+
:param on_exception: handler called when exception occurs. will be passed the captured
114+
exception as an argument. further retries are stopped when handler
115+
returns True. default: None
103116
:returns: the result of the f function.
104117
"""
105118
args = fargs if fargs else list()
106119
kwargs = fkwargs if fkwargs else dict()
107120
return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger,
108-
log_traceback=log_traceback)
121+
log_traceback, on_exception)

tests/test_retry.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,17 @@ def f(value=0):
189189
assert f_mock.call_count == 1
190190

191191

192+
def test_call_on_exception():
193+
exception = RuntimeError()
194+
f_mock = MagicMock(side_effect=exception)
195+
callback_mock = MagicMock()
196+
try:
197+
retry_call(f_mock, tries=1, on_exception=callback_mock)
198+
except RuntimeError:
199+
pass
200+
callback_mock.assert_called_once_with(exception)
201+
202+
192203
def test_logs_function_details(monkeypatch):
193204
mock_sleep_time = [0]
194205

0 commit comments

Comments
 (0)