解决方案 1
retry_on_exception
您可以将其更改为基于返回 via ,而不是使用引发的异常作为重试 via 的基础retry_on_result
。在这里,我们可以传递self
参数。
- 将要重试的整个功能包装在 try-except 块中
- 捕获所有异常
- 如果发生异常,则将异常的详细信息和对对象的类引用包装起来并返回该对象。
- 然后配置的接收器
retry_on_result
将接收对象并可以采取相应的行动。
- 如果没有发生异常,照常进行。返回任何东西或不返回。
- 配置的接收器
retry_on_result
仍然会收到响应,但应该忽略并且不会重试。
from dataclasses import dataclass
import random
from typing import Any
from retrying import retry
# This could be a dataclass or just an ordinary class
@dataclass
class RetryInfo:
exception: Exception
class_reference: Any
def retry_if_db_error_or_passwd_change(retry_info):
"""Return True if we should retry, False otherwise."""
if not isinstance(retry_info, RetryInfo):
# If the response isn't intended for the retry mechanism, ignore and just continue.
print("No retry needed")
return False
print("Evaluate retry for:", type(retry_info.exception), retry_info.class_reference.value)
# Let's say we will stop the retries if there is a RuntimeError
return not isinstance(retry_info.exception, RuntimeError)
class MyClass:
def __init__(self, value):
self.value = value
@retry(retry_on_result=retry_if_db_error_or_passwd_change)
def add_request_to_db(self, raise_exception):
try:
# Wrap the whole code inside a try-except block to catch all exceptions.
print("Called add_request_to_db")
self.value += 1
if raise_exception:
raise random.choice([IndexError, KeyError, RuntimeError, TypeError, ValueError])
except Exception as error:
# Instead of raising the exception to trigger a retry, just return the contained values.
return RetryInfo(error, self)
print("Call 1...")
MyClass(0).add_request_to_db(True)
print("\n==========\n")
print("Call 2...")
MyClass(0).add_request_to_db(False)
$ python3 script.py
Call 1...
Called add_request_to_db
Evaluate retry for: <class 'ValueError'> 1
Called add_request_to_db
Evaluate retry for: <class 'TypeError'> 2
Called add_request_to_db
Evaluate retry for: <class 'ValueError'> 3
Called add_request_to_db
Evaluate retry for: <class 'TypeError'> 4
Called add_request_to_db
Evaluate retry for: <class 'KeyError'> 5
Called add_request_to_db
Evaluate retry for: <class 'TypeError'> 6
Called add_request_to_db
Evaluate retry for: <class 'RuntimeError'> 7
==========
Call 2...
Called add_request_to_db
No retry needed
- 对于“Call 1”,
retry_if_db_error_or_passwd_change
能够接收到异常和对的引用,self
因为它正确地打印了(递增数字)的当前值self.value
并且能够在RuntimeError
发生时停止。
- 对于“调用 2”,如果没有引发异常,则行为不会改变,也不会重试。
解决方案 2
这是受到@blhsing 答案的启发。在类初始化期间包装类方法,以便您可以注入类引用self
。
import random
from retrying import retry
def retry_if_db_error_or_passwd_change(exception, class_reference):
"""Return True if we should retry, False otherwise."""
print("Evaluate retry for:", type(exception), class_reference.value)
# Let's say we will just retry if any kind of exception occurred
return isinstance(exception, Exception)
class MyClass:
def __init__(self, value):
self.value = value
# Decorate functions to be retried
retry_decorator = retry(
retry_on_exception=lambda exc: retry_if_db_error_or_passwd_change(exc, self),
)
self.add_request_to_db = retry_decorator(self.add_request_to_db)
def add_request_to_db(self, anything):
self.value += 1
chosen_exc = random.choice([IndexError, KeyError, RuntimeError, TypeError, None])
if chosen_exc:
print("Called add_request_to_db failed")
raise chosen_exc
else:
print("Called add_request_to_db successful")
MyClass(0).add_request_to_db(None)
$ python3 script2.py
Called add_request_to_db failed
Evaluate retry for: <class 'TypeError'> 1
Called add_request_to_db failed
Evaluate retry for: <class 'KeyError'> 2
Called add_request_to_db failed
Evaluate retry for: <class 'TypeError'> 3
Called add_request_to_db failed
Evaluate retry for: <class 'RuntimeError'> 4
Called add_request_to_db failed
Evaluate retry for: <class 'RuntimeError'> 5
Called add_request_to_db successful
- 类引用被正确传递,因为它能够打印 self.value 的当前值(增加的数字),并且当不再引发异常时重试停止。