Повтор Python с использованием цепкости без декоратора
Я пытаюсь сделать попытку, используя цепкость (без декоратора). Мой код выглядит как описано здесь.
import logging
from tenacity import retry
import tenacity
def print_msg():
try:
logging.info('Hello')
logging.info("World")
raise Exception('Test error')
except Exception as e:
logging.error('caught error')
raise e
if __name__ == '__main__':
logging.basicConfig(
format='%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
datefmt='%d-%m-%Y:%H:%M:%S',
level=logging.INFO)
logging.info('Starting')
try:
r = tenacity.Retrying(
tenacity.stop_after_attempt(2),
tenacity.wait_incrementing(start=10, increment=100, max=1000),
reraise=True
)
try:
r.call(print_msg)
except Exception:
logging.error('Test error 2')
except Exception:
logging.error('Received Exception')
При выполнении вышеуказанного кода. Вывод выглядит как ниже без повторов
/Users/dmanna/PycharmProjects/demo/venv/bin/python /Users/dmanna/PycharmProjects/demo/retrier.py
25-11-2018:00:29:47,140 INFO [retrier.py:21] Starting
25-11-2018:00:29:47,140 INFO [retrier.py:8] Hello
25-11-2018:00:29:47,140 INFO [retrier.py:9] World
25-11-2018:00:29:47,140 ERROR [retrier.py:12] caught error
25-11-2018:00:29:47,141 ERROR [retrier.py:31] Test error 2
Process finished with exit code 0
Может кто-нибудь сообщить мне, что происходит не так, как я не вижу повторов в приведенном выше коде?
3 ответа
Здесь ответили Перекрестный ответ
Хм, я не думаю, что вы используете API здесь правильно:
r = tenacity.Retrying( tenacity.stop_after_attempt(2), tenacity.wait_incrementing(start=10, increment=100, max=1000), reraise=True )
Это переводится как:
r = tenacity.Retrying( sleep=tenacity.stop_after_attempt(2), stop=tenacity.wait_incrementing(start=10, increment=100, max=1000), reraise=True )
Который не собирается делать то, что вы хотите.
Ты хочешь:
r = tenacity.Retrying( stop=tenacity.stop_after_attempt(2), wait=tenacity.wait_incrementing(start=10, increment=100, max=1000), reraise=True )
Примечание для пользователей Python 3.4+ (который поддерживает аннотации): для явного принудительного повторения попытки в Tenacity обычно используется аннотирование его простым @retry
а затем поднять специальное исключение TryAgain
.
@retry
def do_something():
result = something_else()
if result == 23:
raise TryAgain
Этот подход, аналогичный ответу для Python 2.7 на этой странице, также можно реализовать:
import tenacity
def do_something():
result = something_else()
if result == 23:
raise TryAgain
r = tenacity.Retrying(
stop=tenacity.stop_after_attempt(2),
wait=tenacity.wait_incrementing(start=10, increment=100, max=1000)
)
r.wraps(do_something)
Дополнительные сведения см. В документации API о том, следует ли повторить попытку или об источнике, определяющем аннотации.
не используя упорство и не без декоратора, но выслушайте меня: вот хитрый способ повторять функции на лету без необходимости украшать какое-либо определение функции с помощью@retry
. У меня возникла проблема: пара пакетов вызывала проблемы, а один из классов был слишком запутанным, чтобы пытаться перезаписать методы класса с наследованием:
def retry(times=2, wait=1, method=''):
def outer_wrapper(function):
@functools.wraps(function)
def inner_wrapper(*args, **kwargs):
final_excep = None
for i in range(times):
final_excep = None
try:
res = function(*args, **kwargs)
return res
except Exception as e:
final_excep = e
print(f'error with {method}: {e}')
time.sleep(wait * (i/2))
pass
if final_excep is not None:
return False
return inner_wrapper
return outer_wrapper
def resilient_function(func, *args, **kwargs):
return run_sheet_function(functools.partial(func, *args, **kwargs))
@retry(times=3, wait=3, method='run sheet function')
def run_sheet_function(func):
return func()
##original function call
df = spread.sheet_to_df(index=0, start_row=2, formula_columns=['Monthly Service'])
##new call
df = resilient_function(spread.sheet_to_df, index=0, start_row=2, formula_columns=['Monthly Service'])