使用run_until_complete运行时,重试不起作用

时间:2018-11-29 10:40:46

标签: python retrying

首先,我像这样运行代码,重试工作正常。

# -*- 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

如所示,重试在第二个代码中不起作用。区别在于我将call()放入loop.run_until_complete方法中,如何解决此问题?

1 个答案:

答案 0 :(得分:0)

相关的区别是,在第二个代码片段中,您不再装饰一个函数,而是一个协程。它们仅在事件循环中执行后返回。

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协程的结果。我已经采用this basic but functional asnyc retry decorator来根据您函数的返回值做出决定,就像retrying中的函数一样。

请注意,内部wrapper函数是一个协程,它await装饰了协程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语句以外,其余代码均未更改。