[英]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.