Повтор не работает при запуске с run_until_complete - PullRequest
0 голосов
/ 29 ноября 2018

Во-первых, я запускаю код, подобный этому, и повторная попытка работает правильно.

# -*- 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 Ответ

0 голосов
/ 29 ноября 2018

Отличие заключается в том, что во втором фрагменте кода вы больше не украшаете функцию, а получаете сопрограмму.Они возвращаются только после выполнения в цикле событий.

Добавление отладочной печати для 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.

...