簡體   English   中英

包裝 asyncio.gather 時出現異常

[英]Exception when wrapping asyncio.gather

我對 asyncio.gather 進行了如下試驗:

async def some_work(work_name, timeout, raise_exception=False):
    """Do some work"""
    print(f"Start {work_name}")
    await asyncio.sleep(timeout)
    if raise_exception:
        raise RuntimeError(f"{work_name} raise an exception")
    print(f"Finish {work_name}")


async def main():
    try:
        await asyncio.gather(
            some_work("work1", 3),
            some_work("work2", 1),
            some_work("work3", 2),
            asyncio.gather(
                some_work("work4", 3),
                some_work("work5", 1, raise_exception=True),
                some_work("work6", 2)
            )
        )

    except RuntimeError as error:
        print(error)


if __name__ == '__main__':
    asyncio.run(main())

在某個時候,我決定像這樣對 asyncio.gather 進行包裝:

# Yes I know, concurrently really
def in_parallel(*aws, loop=None, return_exceptions=False):
    return asyncio.gather(aws, loop, return_exceptions)

並像這樣使用它:

async def main():
    try:
        await in_parallel(
            some_work("work1", 3),
            some_work("work2", 1),
            some_work("work3", 2),
            in_parallel(
                some_work("work4", 3),
                some_work("work5", 1, raise_exception=True),
                some_work("work6", 2)
            )
        )

    except RuntimeError as error:
        print(error)


if __name__ == '__main__':
    asyncio.run(main())

並得到一堆錯誤:

D:/Archive/Projects/PycharmProjects/test/asyncio_gather.py:34: RuntimeWarning: coroutine 'some_work' is never awaited in_parallel(RuntimeWarning: Enable tracemalloc to get the object allocation traceback Traceback (最近一次調用最后): 文件 "D: /Archive/Projects/PycharmProjects/test/asyncio_gather.py”,第 46 行,在 asyncio.run(main()) 文件“C:\Program Files\Python38\lib\asyncio\runners.py”,第 43 行,運行中返回 loop.run_until_complete(main) 文件“C:\Program Files\Python38\lib\asyncio\base_events.py”,第 612 行,在 run_until_complete 返回 future.result() 文件“D:/Archive/Projects/PycharmProjects/test/ asyncio_gather.py”,第 34 行,in_parallel 主要(文件“D:/Archive/Projects/PycharmProjects/test/asyncio_gather.py”,第 14 行,in_parallel 返回 asyncio.gather(aws,循環,return_exceptions)文件“C: \Program Files\Python38\lib\asyncio\tasks.py",第 806 行,聚集 fut = ensure_future(arg, loop=loop) 文件 "C:\Program Files\Python38\lib\asyncio\tasks.py", 第 673 行,在 ensure_future raise TypeError('An asyncio.Future, a coroutine or an awaitable is ' TypeError: An asyncio.Future, a coroutine or an awaitable is required sys:1: RuntimeWarning: coroutine 'some_work' was never awaited

誰能解釋為什么? 它只是一個包裝!

誰能解釋為什么? 它只是一個包裝!

包裝器具有正確的簽名,但它沒有正確調用asyncio.gather

def in_parallel(*aws, loop=None, return_exceptions=False):
    return asyncio.gather(aws, loop, return_exceptions)

asyncio.gather期望 awaitables 作為位置 arguments 傳遞,這就是您在代碼的第一個版本中調用它的方式。 當您從包裝器中調用它時,您始終會准確地傳遞三個位置 arguments: aws (包含傳遞給in_parallel的 awaitables 元組)、 loop (調用時始終為None )和return_exceptions (布爾值)。 這些都不是實際的可等待對象,因此如果gather嘗試對收到的“等待對象”執行某些操作(例如將它們轉換為期貨),就會引發異常。

gather調用in_parallel的正確方法是使用*運算符將aws的每個元素作為單獨的位置參數傳遞,並將loop作為關鍵字參數傳遞,就像您已經在使用return_exceptions一樣:

def in_parallel(*aws, loop=None, return_exceptions=False):
    return asyncio.gather(*aws, loop=loop, return_exceptions=return_exceptions)

通過此修改,您的代碼可以按預期工作。 最后,請注意顯式loop參數已棄用,因此您可以從包裝器中省略它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM