@@ -74,19 +74,49 @@ def setup_logging(level=None):
7474 S3_RETRYABLE_ERRORS += (ClientPayloadError ,)
7575
7676def add_retryable_error (exc ):
77- """Add an exception type to the list of retryable S3 errors."""
77+ """
78+ Add an exception type to the list of retryable S3 errors.
79+
80+ Parameters
81+ ----------
82+ exc : Exception
83+ The exception type to add to the retryable errors.
84+
85+ Examples
86+ ----------
87+ >>> class MyCustomError(Exception): # doctest: +SKIP
88+ ... pass # doctest: +SKIP
89+ >>> add_retryable_error(MyCustomError) # doctest: +SKIP
90+ """
7891 global S3_RETRYABLE_ERRORS
7992 S3_RETRYABLE_ERRORS += (exc ,)
8093
81- _CUSTOM_ERROR_HANDLER = lambda e : False
94+ CUSTOM_ERROR_HANDLER = lambda _ : False
8295def set_custom_error_handler (func ):
8396 """Set a custom error handler function for S3 retryable errors.
8497
8598 The function should take an exception instance as its only argument,
8699 and return True if the operation should be retried, or False otherwise.
100+ This can also be used for custom behavior on `ClientError` exceptions,
101+ such as retrying other patterns.
102+
103+ Parameters
104+ ----------
105+ func : callable[[Exception], bool]
106+ The custom error handler function.
107+
108+ Examples
109+ ----------
110+ >>> def my_handler(e): # doctest: +SKIP
111+ ... return isinstance(e, MyCustomError) and "some condition" in str(e) # doctest: +SKIP
112+ >>> set_custom_error_handler(my_handler) # doctest: +SKIP
113+
114+ >>> def another_handler(e): # doctest: +SKIP
115+ ... return isinstance(e, ClientError) and "Throttling" in str(e) # doctest: +SKIP
116+ >>> set_custom_error_handler(another_handler) # doctest: +SKIP
87117 """
88- global _CUSTOM_ERROR_HANDLER
89- _CUSTOM_ERROR_HANDLER = func
118+ global CUSTOM_ERROR_HANDLER
119+ CUSTOM_ERROR_HANDLER = func
90120
91121_VALID_FILE_MODES = {"r" , "w" , "a" , "rb" , "wb" , "ab" }
92122
@@ -124,6 +154,7 @@ def set_custom_error_handler(func):
124154async def _error_wrapper (func , * , args = (), kwargs = None , retries ):
125155 if kwargs is None :
126156 kwargs = {}
157+ err = None
127158 for i in range (retries ):
128159 wait_time = min (1.7 ** i * 0.1 , 15 )
129160
@@ -150,18 +181,18 @@ async def _error_wrapper(func, *, args=(), kwargs=None, retries):
150181 if matched :
151182 await asyncio .sleep (wait_time )
152183 else :
153- should_retry = _CUSTOM_ERROR_HANDLER (e )
184+ should_retry = CUSTOM_ERROR_HANDLER (e )
154185 if should_retry :
155186 await asyncio .sleep (wait_time )
156187 else :
157188 break
158189 except Exception as e :
159- should_retry = _CUSTOM_ERROR_HANDLER (e )
190+ err = e
191+ should_retry = CUSTOM_ERROR_HANDLER (e )
160192 if should_retry :
161193 await asyncio .sleep (wait_time )
162194 else :
163195 logger .debug ("Nonretryable error: %s" , e )
164- err = e
165196 break
166197
167198 if "'coroutine'" in str (err ):
0 commit comments