Повтор не работает при запуске с 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 функция сопрограммы, которая awaits результат декорированной сопрограммы 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 функция.

Другие вопросы по тегам