@@ -17,16 +17,14 @@ def retry_on_exception(
17
17
suppress_all_errors : bool = False ,
18
18
logger : Optional [Logger ] = None ,
19
19
default_return_on_suppression : Optional [Any ] = None ,
20
+ wrap_error_message_in : Optional [str ] = None ,
20
21
) -> Optional [Any ]:
21
22
"""
22
- A decorator **for instance methods** to be retried on failure.
23
+ A decorator for instance methods or standalone functions that makes them
24
+ retry on failure and allows to specify on which types of exceptions the
25
+ function should and should not retry.
23
26
24
- Warnings:
25
- If the variable `check_message_contains` is supplied and the error message
26
- contains any of the strings provided, the error will be suppressed
27
- and default value returned.
28
-
29
- If the variable `suppress_all_errors` is supplied and set to True,
27
+ NOTE: If the argument `suppress_all_errors` is supplied and set to True,
30
28
the error will be suppressed and default value returned.
31
29
32
30
Args:
@@ -37,27 +35,41 @@ def retry_on_exception(
37
35
if their supertype appears in `exception_types` or the only exceptions to
38
36
not retry on if no `exception_types` are specified.
39
37
40
- check_message_contains: A list of strings to be provided. If the error
41
- message contains any one of these messages, the exception will
42
- be suppressed.
38
+ check_message_contains: A list of strings, against which to match error
39
+ messages. If the error message contains any one of these strings,
40
+ the exception will cause a retry. NOTE: This argument works in
41
+ addition to `exception_types`; if those are specified, only the
42
+ specified types of exceptions will be caught and retried on if they
43
+ contain the strings provided as `check_message_contains`.
43
44
44
- retries: Number of retries.
45
+ retries: Number of retries to perform .
45
46
46
- suppress_all_errors: A flag which will allow you to suppress all exceptions
47
- of the type provided after all retries have been exhausted.
47
+ suppress_all_errors: If true, after all the retries are exhausted, the
48
+ error will still be suppressed and `default_return_on_suppresion`
49
+ will be returned from the function. NOTE: If using this argument,
50
+ the decorated function may not actually get fully executed, if
51
+ it consistently raises an exception.
48
52
49
53
logger: A handle for the logger to be used.
50
54
51
- default_return_on_suppression: If the error is suppressed, then the default
52
- value to be returned once all retries are exhausted.
55
+ default_return_on_suppression: If the error is suppressed after all the
56
+ retries, then this default value will be returned from the function.
57
+ Defaults to None.
58
+
59
+ wrap_error_message_in: If raising the error message after all the retries,
60
+ a string wrapper for the error message (useful for making error
61
+ messages more user-friendly). NOTE: Format of resulting error will be:
62
+ "<wrap_error_message_in>: <original_error_type>: <original_error_msg>",
63
+ with the stack trace of the original message.
64
+
53
65
"""
54
66
55
67
def func_wrapper (func ):
56
68
@functools .wraps (func )
57
- def actual_wrapper (self , * args , ** kwargs ):
69
+ def actual_wrapper (* args , ** kwargs ):
58
70
retriable_exceptions = exception_types
59
71
if exception_types is None :
60
- # If no exception type provided, we catch all errors
72
+ # If no exception type provided, we catch all errors.
61
73
retriable_exceptions = (Exception ,)
62
74
if not isinstance (retriable_exceptions , tuple ):
63
75
raise ValueError ("Expected a tuple of exception types." )
@@ -75,17 +87,15 @@ def actual_wrapper(self, *args, **kwargs):
75
87
"`exception_types` and `no_retry_on_exception_types`."
76
88
)
77
89
78
- suppress_errors = False
79
- if suppress_all_errors or (
90
+ # `suppress_all_errors` could be a flag to the underlying function
91
+ # when used on instance methods.
92
+ suppress_errors = suppress_all_errors or (
80
93
"suppress_all_errors" in kwargs and kwargs ["suppress_all_errors" ]
81
- ):
82
- # If we are provided with a flag to suppress all errors
83
- # inside either the function kwargs or the decorator parameters
84
- suppress_errors = True
94
+ )
85
95
86
96
for i in range (retries ):
87
97
try :
88
- return func (self , * args , ** kwargs )
98
+ return func (* args , ** kwargs )
89
99
except no_retry_on_exception_types or ():
90
100
raise
91
101
except retriable_exceptions as err : # Exceptions is a tuple.
@@ -102,9 +112,17 @@ def actual_wrapper(self, *args, **kwargs):
102
112
# In this case, the error is just logged, suppressed and default
103
113
# value returned
104
114
if logger is not None :
105
- logger .exception (err )
115
+ logger .exception (
116
+ wrap_error_message_in
117
+ ) # Automatically logs `err` and its stack trace.
106
118
continue
107
- raise
119
+ if not wrap_error_message_in :
120
+ raise
121
+ else :
122
+ msg = (
123
+ f"{ wrap_error_message_in } : { type (err ).__name__ } : { str (err )} "
124
+ )
125
+ raise type (err )(msg ).with_traceback (err .__traceback__ )
108
126
# If we are here, it means the retries were finished but
109
127
# The error was somehow suppressed. Hence return the default value provided
110
128
return default_return_on_suppression
0 commit comments