Повтор не работает при запуске с run_until_complete
Сначала я запускаю такой код, и повторная попытка работает правильно.
# -*- coding:utf-8 -*-
from retrying import retry
import asyncio
import time
num = 0;
def retry_if_result_none(result):
return result is None
@retry(retry_on_result=retry_if_result_none)
def get_result():
global num;
num += 1;
if num < 10:
print('Retry.....');
return None;
else:
return True;
time.sleep(1);
def call():
end = get_result();
if end:
print('ok');
else:
print('over')
if __name__ == '__main__':
call();
Output:
Retry.....
Retry.....
Retry.....
Retry.....
Retry.....
Retry.....
Retry.....
Retry.....
Retry.....
ok
Во-вторых, я редактирую код и запускаю снова, но получаю результат разницы.
# -*- coding:utf-8 -*-
from retrying import retry
import asyncio
import time
num = 0;
def retry_if_result_none(result):
# print("retry_if_result_none")
return result is None
@retry(retry_on_result=retry_if_result_none)
async def get_result():
global num;
num += 1;
if num < 10:
print('Retry.....');
return None;
else:
return True;
time.sleep(1);
async def call():
end = await get_result();
if end:
print('ok');
else:
print('over')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(call())
Output:
Retry.....
over
Как показано, повтор не работает во втором коде. Разница в том, что я поместил вызов () в метод loop.run_until_complete. Как решить эту проблему?
1 ответ
Существенное отличие состоит в том, что во втором фрагменте кода вы больше не украшаете функцию, а получаете сопрограмму. Они возвращаются только после выполнения в цикле событий.
Добавление отладочной печати для result
к вашей функции проверки и запуск ее с вашим примером синхронного кода показывает ожидаемый результат:
def retry_if_result_none(result):
print(result)
return result is None
Retry.....
None
Retry.....
None
True # Note: I have set the condition to num < 3
ok
Если вы делаете то же самое с вашей асинхронной версией, вы видите проблему:
<coroutine object get_result at 0x0322EF90>
Retry.....
over
Так result
на самом деле сама программа, а не ее результат. Отсюда твой retry_if_result_none
функция возвращает False
и цикл повторения завершается после первой итерации.
Это в основном проблема времени. Ваш синхронный декоратор не синхронизирован (очень каламбур) с асинхронным выполнением сопрограммы в цикле обработки событий.
Вам придется использовать асинхронный декоратор, чтобы иметь возможность await
результат сопрограммы. Я принял этот базовый, но функциональный asnyc retry decorator, чтобы принимать решение на основе возвращаемого значения вашей функции, например, из retrying
делает.
Обратите внимание, что внутренний wrapper
функция сопрограммы, которая await
s результат декорированной сопрограммы get_result
,
def tries(func):
def func_wrapper(f):
async def wrapper(*args, **kwargs):
while True:
try:
if func(await f(*args, **kwargs)):
continue
else:
break
except Exception as exc:
pass
return True
return wrapper
return func_wrapper
@tries(retry_if_result_none)
async def get_result():
[...]
Использование этого в вашем асинхронном коде дает ожидаемый результат:
Retry.....
None
Retry.....
None
[...]
Retry.....
None
True
ok
Остальная часть вашего кода не была изменена, за исключением включения декоратора get_result
и упомянутый print
заявление в retry_if_result_none
функция.