将多个参数传递给 retry_on_exception 在 python 中重试的参数
Passing multiple arguments to retry_on_exception argument of retrying in python
我有一个用例,如果出现任何异常,需要重试向 table 添加一行的方法。我正在使用重试的@retry 装饰器来实现这一点。
其中一种情况是更改了数据库的密码。我正在使用 arg retry_on_exception 来捕获任何异常。我面临的问题是,我希望密码获取发生在我传递给 retry_on_exception 的方法中。
到目前为止我的代码,
@retry(stop_max_attempt_number=3, retry_on_exception=retry_if_db_error_or_passwd_change)
def add_request_to_db(self, req_id):
with make_session(self.session_maker) as session:
session.merge(RequestHolder(request_id=req_id))
session.commit()
和retry_if_db_error_or_passwd_change就像
def retry_if_db_error_or_passwd_change(exception, class_reference):
is_db_exception = isinstance(exception, DBRetryExceededException)
is_operational_error = isinstance(exception, OperationalError)
if is_db_exception:
return True
elif is_operational_error or 'Access denied ' in exception:
fetch_password(class_reference)
return True
我的问题是,在将可调用对象传递给 retry_on_exception 时,如何将 class 引用与 retry_if_db_error_or_passwd_change 一起发送?
您可以将 class 引用保留为使用此附加参数调用 retry_if_db_error_or_passwd_change
的包装函数的默认参数:
# assign cls with the desired class reference first
@retry(
stop_max_attempt_number=3,
retry_on_exception=lambda exc, cls=cls: retry_if_db_error_or_passwd_change(exc, cls)
)
def add_request_to_db(self, req_id):
...
解决方案 1
不是通过 retry_on_exception
使用引发的异常作为重试的基础,您可以将其更改为基于通过 retry_on_result
的 return。在这里,我们可以传递 self
参数。
- 将要重试的整个功能包装在 try-except 块中
- 捕获所有异常
- 如果发生异常,将异常的详细信息和对对象的 class 引用和 return 对象包装起来。
retry_on_result
的已配置接收器随后将接收该对象并可以相应地执行操作。
- 如果没有异常发生,照常进行。 Return 任何东西或 none。
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
- 对于“调用 1”,
retry_if_db_error_or_passwd_change
能够接收到异常和对 self
的引用,因为它正确地打印了 self.value
的当前值(递增的数字)并且能够在发生 RuntimeError
时停止。
- 对于“调用 2”,如果没有引发异常,则行为不会改变,不会进行重试。
解决方案 2
灵感来自于@blhsing 的回答。在 class 初始化期间包装 class 方法,以便您可以注入 class 引用 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
- class 引用被正确传递,因为它能够打印 self.value 的当前值(增加的数字)并且当不再引发异常时重试停止。
我有一个用例,如果出现任何异常,需要重试向 table 添加一行的方法。我正在使用重试的@retry 装饰器来实现这一点。
其中一种情况是更改了数据库的密码。我正在使用 arg retry_on_exception 来捕获任何异常。我面临的问题是,我希望密码获取发生在我传递给 retry_on_exception 的方法中。
到目前为止我的代码,
@retry(stop_max_attempt_number=3, retry_on_exception=retry_if_db_error_or_passwd_change)
def add_request_to_db(self, req_id):
with make_session(self.session_maker) as session:
session.merge(RequestHolder(request_id=req_id))
session.commit()
和retry_if_db_error_or_passwd_change就像
def retry_if_db_error_or_passwd_change(exception, class_reference):
is_db_exception = isinstance(exception, DBRetryExceededException)
is_operational_error = isinstance(exception, OperationalError)
if is_db_exception:
return True
elif is_operational_error or 'Access denied ' in exception:
fetch_password(class_reference)
return True
我的问题是,在将可调用对象传递给 retry_on_exception 时,如何将 class 引用与 retry_if_db_error_or_passwd_change 一起发送?
您可以将 class 引用保留为使用此附加参数调用 retry_if_db_error_or_passwd_change
的包装函数的默认参数:
# assign cls with the desired class reference first
@retry(
stop_max_attempt_number=3,
retry_on_exception=lambda exc, cls=cls: retry_if_db_error_or_passwd_change(exc, cls)
)
def add_request_to_db(self, req_id):
...
解决方案 1
不是通过 retry_on_exception
使用引发的异常作为重试的基础,您可以将其更改为基于通过 retry_on_result
的 return。在这里,我们可以传递 self
参数。
- 将要重试的整个功能包装在 try-except 块中
- 捕获所有异常
- 如果发生异常,将异常的详细信息和对对象的 class 引用和 return 对象包装起来。
retry_on_result
的已配置接收器随后将接收该对象并可以相应地执行操作。
- 如果没有异常发生,照常进行。 Return 任何东西或 none。
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
- 对于“调用 1”,
retry_if_db_error_or_passwd_change
能够接收到异常和对self
的引用,因为它正确地打印了self.value
的当前值(递增的数字)并且能够在发生RuntimeError
时停止。 - 对于“调用 2”,如果没有引发异常,则行为不会改变,不会进行重试。
解决方案 2
灵感来自于@blhsing 的回答。在 class 初始化期间包装 class 方法,以便您可以注入 class 引用 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
- class 引用被正确传递,因为它能够打印 self.value 的当前值(增加的数字)并且当不再引发异常时重试停止。