3030from tenacity import stop
3131from tenacity import wait
3232from tenacity .retry import retry_base
33+ from typing_extensions import Self
34+
35+ import framework .errors
3336
3437retryers_logger = logging .getLogger (__name__ )
3538# Type aliases
@@ -229,6 +232,8 @@ class RetryError(tenacity.RetryError):
229232
230233 last_attempt : tenacity .Future
231234 note : str = ""
235+ _message : str = ""
236+ _print_last_trace : bool = True
232237
233238 def __init__ (
234239 self ,
@@ -238,35 +243,55 @@ def __init__(
238243 attempts : int = 0 ,
239244 check_result : Optional [CheckResultFn ] = None ,
240245 note : str = "" ,
246+ print_last_trace : bool = True ,
241247 ):
242248 last_attempt : tenacity .Future = retry_state .outcome
243249 super ().__init__ (last_attempt )
250+ self ._print_last_trace = print_last_trace
251+
252+ message = f"Retry error"
244253
245- self .message = f"Retry error"
246254 if retry_state .fn is None :
247255 # Context manager
248- self . message += f":"
256+ message += f":"
249257 else :
250258 callback_name = tenacity_utils .get_callback_name (retry_state .fn )
251- self .message += f" calling { callback_name } :"
259+ message += f" calling { callback_name } :"
260+
252261 if timeout :
253- self . message += f" timeout { timeout } (h:mm:ss) exceeded"
262+ message += f" timeout { timeout } (h:mm:ss) exceeded"
254263 if attempts :
255- self .message += " or"
264+ message += " or"
265+
256266 if attempts :
257- self . message += f" { attempts } attempts exhausted"
267+ message += f" { attempts } attempts exhausted"
258268
259- self . message += "."
269+ message += "."
260270
261271 if last_attempt .failed :
262272 err = last_attempt .exception ()
263- self . message += f" Last exception : { type (err ).__name__ } : { err } "
273+ message += f" Last Retry Attempt Error : { type (err ).__name__ } "
264274 elif check_result :
265- self .message += " Check result callback returned False."
275+ message += " Check result callback returned False."
276+
277+ self ._message = message
266278
267279 if note :
268280 self .add_note (note )
269281
282+ @property
283+ def message (self ):
284+ message = ""
285+
286+ # TODO(sergiitk): consider if we want to have print-by-default
287+ # and/or ignore-by-default exception lists.
288+ if cause := self .exception () and self ._print_last_trace :
289+ cause_trace = framework .errors .format_error_with_trace (cause )
290+ message += f"Last Retry Attempt Traceback:\n { cause_trace } \n \n "
291+
292+ message += self ._message
293+ return message
294+
270295 def result (self , * , default = None ):
271296 return (
272297 self .last_attempt .result ()
@@ -295,6 +320,10 @@ def reason_str(self):
295320 def _exception_str (cls , err : Optional [BaseException ]) -> str :
296321 return f"{ type (err ).__name__ } : { err } " if err else "???"
297322
323+ def with_last_trace (self , enabled : bool = True ) -> Self :
324+ self ._print_last_trace = enabled
325+ return self
326+
298327 # TODO(sergiitk): Remove in py3.11, this will be built-in. See PEP 678.
299328 def add_note (self , note : str ):
300329 self .note = note
0 commit comments