Add new @Retry() decorator (#7854)
Signed-off-by: Glenn Jocher <glenn.jocher@ultralytics.com>
This commit is contained in:
parent
5f00fbd227
commit
1435f0e9de
9 changed files with 372 additions and 269 deletions
|
|
@ -9,6 +9,7 @@ import re
|
|||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
|
@ -721,9 +722,19 @@ def remove_colorstr(input_string):
|
|||
|
||||
class TryExcept(contextlib.ContextDecorator):
|
||||
"""
|
||||
YOLOv8 TryExcept class.
|
||||
Ultralytics TryExcept class. Use as @TryExcept() decorator or 'with TryExcept():' context manager.
|
||||
|
||||
Use as @TryExcept() decorator or 'with TryExcept():' context manager.
|
||||
Examples:
|
||||
As a decorator:
|
||||
>>> @TryExcept(msg="Error occurred in func", verbose=True)
|
||||
>>> def func():
|
||||
>>> # Function logic here
|
||||
>>> pass
|
||||
|
||||
As a context manager:
|
||||
>>> with TryExcept(msg="Error occurred in block", verbose=True):
|
||||
>>> # Code block here
|
||||
>>> pass
|
||||
"""
|
||||
|
||||
def __init__(self, msg="", verbose=True):
|
||||
|
|
@ -742,6 +753,64 @@ class TryExcept(contextlib.ContextDecorator):
|
|||
return True
|
||||
|
||||
|
||||
class Retry(contextlib.ContextDecorator):
|
||||
"""
|
||||
Retry class for function execution with exponential backoff.
|
||||
|
||||
Can be used as a decorator or a context manager to retry a function or block of code on exceptions, up to a
|
||||
specified number of times with an exponentially increasing delay between retries.
|
||||
|
||||
Examples:
|
||||
Example usage as a decorator:
|
||||
>>> @Retry(times=3, delay=2)
|
||||
>>> def test_func():
|
||||
>>> # Replace with function logic that may raise exceptions
|
||||
>>> return True
|
||||
|
||||
Example usage as a context manager:
|
||||
>>> with Retry(times=3, delay=2):
|
||||
>>> # Replace with code block that may raise exceptions
|
||||
>>> pass
|
||||
"""
|
||||
|
||||
def __init__(self, times=3, delay=2):
|
||||
"""Initialize Retry class with specified number of retries and delay."""
|
||||
self.times = times
|
||||
self.delay = delay
|
||||
self._attempts = 0
|
||||
|
||||
def __call__(self, func):
|
||||
"""Decorator implementation for Retry with exponential backoff."""
|
||||
|
||||
def wrapped_func(*args, **kwargs):
|
||||
self._attempts = 0
|
||||
while self._attempts < self.times:
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
self._attempts += 1
|
||||
print(f"Retry {self._attempts}/{self.times} failed: {e}")
|
||||
if self._attempts >= self.times:
|
||||
raise e
|
||||
time.sleep(self.delay * (2**self._attempts)) # exponential backoff delay
|
||||
|
||||
return wrapped_func
|
||||
|
||||
def __enter__(self):
|
||||
"""Enter the runtime context related to this object."""
|
||||
self._attempts = 0
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
"""Exit the runtime context related to this object with exponential backoff."""
|
||||
if exc_type is not None:
|
||||
self._attempts += 1
|
||||
if self._attempts < self.times:
|
||||
print(f"Retry {self._attempts}/{self.times} failed: {exc_value}")
|
||||
time.sleep(self.delay * (2**self._attempts)) # exponential backoff delay
|
||||
return True # Suppresses the exception and retries
|
||||
return False # Re-raises the exception if retries are exhausted
|
||||
|
||||
|
||||
def threaded(func):
|
||||
"""
|
||||
Multi-threads a target function by default and returns the thread or function result.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue