簡體   English   中英

使用asyncio.gather不會引發內部異常

[英]Inner exception is not being raised using asyncio.gather

使用Python 3.7,我嘗試按照在StackOverflow上找到的示例捕獲異常並重新引發它。 盡管該示例確實有效,但似乎並非在所有情況下都適用。 下面,我有兩個異步Python腳本,它們試圖重新引發異常。 第一個示例有效,它將同時打印內部和外部異常。

import asyncio

class Foo:
    async def throw_exception(self):
        raise Exception("This is the inner exception")

    async def do_the_thing(self):
        try:
            await self.throw_exception()
        except Exception as e:
            raise Exception("This is the outer exception") from e

async def run():
    await Foo().do_the_thing()

def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

if __name__ == "__main__":
    main()

運行此命令將正確輸出以下異常堆棧跟蹤:

$ py test.py
Traceback (most recent call last):
  File "test.py", line 9, in do_the_thing
    await self.throw_exception()
  File "test.py", line 5, in throw_exception
    raise Exception("This is the inner exception")
Exception: This is the inner exception

The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "test.py", line 21, in <module>
    main()
  File "test.py", line 18, in main
    loop.run_until_complete(run())
  File "C:\Python37\lib\asyncio\base_events.py", line 584, in run_until_complete
    return future.result()
  File "test.py", line 14, in run
    await Foo().do_the_thing()
  File "test.py", line 11, in do_the_thing
    raise Exception("This is the outer exception") from e
Exception: This is the outer exception

但是,在我的下一個Python腳本中,我有多個任務要排隊,我想從中獲得類似的異常堆棧跟蹤。 本質上,除了上述堆棧跟蹤,我將被打印3次(以下腳本中的每個任務一次)。 上面和下面的腳本之間的唯一區別是run()函數。

import asyncio

class Foo:
    async def throw_exception(self):
        raise Exception("This is the inner exception")

    async def do_the_thing(self):
        try:
            await self.throw_exception()
        except Exception as e:
            raise Exception("This is the outer exception") from e

async def run():
    tasks = []

    foo = Foo()

    tasks.append(asyncio.create_task(foo.do_the_thing()))
    tasks.append(asyncio.create_task(foo.do_the_thing()))
    tasks.append(asyncio.create_task(foo.do_the_thing()))

    results = await asyncio.gather(*tasks, return_exceptions=True)

    for result in results:
        if isinstance(result, Exception):
            print(f"Unexpected exception: {result}")

def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

if __name__ == "__main__":
    main()

上面的代碼片段產生了令人失望的簡短異常,缺少堆棧跟蹤。

$ py test.py
Unexpected exception: This is the outer exception
Unexpected exception: This is the outer exception
Unexpected exception: This is the outer exception

如果將return_exceptions更改為False ,則將獲得異常和堆棧跟蹤打印一次,然后停止執行,並取消其余兩個任務。 輸出與第一個腳本的輸出相同。 這種方法的缺點是,即使遇到異常,我也要繼續處理任務,然后在所有任務完成時在最后顯示所有異常。

如果不提供return_exceptions=True參數,則asyncio.gather將在第一個異常處停止,因此您的方法是正確的:您需要首先收集所有結果和異常,然后顯示它們。

為了獲得您所缺少的完整堆棧跟蹤,您將需要做的不僅僅是“打印”異常。 看一下stdlib中的traceback模塊,該模塊具有您所需的全部功能: https : //docs.python.org/3/library/traceback.html

您也可以使用logging.exception ,或多或少地做同樣的事情。

暫無
暫無

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

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