2016-02-13 62 views
3

鑑於此示例代碼如何在調試異步函數時檢索原始調用堆棧?

#!/usr/bin/python3 
import asyncio 

async def f1(): 
    await f2() 
async def f2(): 
    try: 
     await asyncio.sleep(1) 
    except BaseException as exc: 
     import pdb;pdb.set_trace() 
     pass 
async def main(): 
    f = asyncio.ensure_future(f1()) 
    await asyncio.sleep(0.5) 
    f.cancel() 
    await f 

loop = asyncio.get_event_loop() 
loop.run_until_complete(main()) 

在斷點處的堆棧跟蹤看起來是這樣的:

(Pdb) w 
    /tmp/t.py(19)<module>() 
-> loop.run_until_complete(main()) 
    /usr/lib/python3.5/asyncio/base_events.py(325)run_until_complete() 
-> self.run_forever() 
    /usr/lib/python3.5/asyncio/base_events.py(295)run_forever() 
-> self._run_once() 
    /usr/lib/python3.5/asyncio/base_events.py(1254)_run_once() 
-> handle._run() 
    /usr/lib/python3.5/asyncio/events.py(125)_run() 
-> self._callback(*self._args) 
    /usr/lib/python3.5/asyncio/tasks.py(293)_wakeup() 
-> self._step(exc) 
    /usr/lib/python3.5/asyncio/tasks.py(241)_step() 
-> result = coro.throw(exc) 
> /tmp/t.py(11)f2() 
-> pass 

這是不好的,因爲我再也看不到F2()最初是從F1被稱爲() 。

如何找回原來的調用堆棧,因爲Python是能夠做到這一點通過簡單的單步?:

$ python3 t.py 
> /tmp/t.py(11)f2() 
-> pass 
(Pdb) s 
--Return-- 
> /tmp/t.py(11)f2()->None 
-> pass 
(Pdb) s 
--Call-- 
> /tmp/t.py(5)f1() 
-> await f2() 
(Pdb) s 
--Return-- 
> /tmp/t.py(4)f1()->None 

回答

2

沒辦法,對不起。

這是不可能的,至少在Python 3.5

UPD

內蟒框架具有至外框fr.f_back)的引用,它使能夠顯示堆棧跟蹤。

但它沒有提及外部協程等待內部的一個。

您的代碼演示了一個非常有趣的案例。

對於簡單的例子:

#!/usr/bin/python3 
import asyncio 
import sys 
import traceback 

async def f1(): 
    await f2() 
async def f2(): 
    import pdb;pdb.set_trace() 

loop = asyncio.get_event_loop() 
loop.run_until_complete(f1()) 

,我們將看到在跟蹤你的協同程序:

(Pdb) w 
    /tmp/3.py(12)<module>() 
-> loop.run_until_complete(f1()) 
    /usr/lib/python3.5/asyncio/base_events.py(325)run_until_complete() 
-> self.run_forever() 
    /usr/lib/python3.5/asyncio/base_events.py(295)run_forever() 
-> self._run_once() 
    /usr/lib/python3.5/asyncio/base_events.py(1254)_run_once() 
-> handle._run() 
    /usr/lib/python3.5/asyncio/events.py(125)_run() 
-> self._callback(*self._args) 
    /usr/lib/python3.5/asyncio/tasks.py(239)_step() 
-> result = coro.send(None) 
    /tmp/3.py(7)f1() 
-> await f2() 
> /tmp/3.py(9)f2()->None 
-> import pdb;pdb.set_trace() 

但在你的情況的情況是不同的:f2()等待sleep()

它的內部工作以下列方式:sleep()返回未來對象f2()收益率(不返回)回f1()再次產生的未來。在這種情況下最上面的呼叫者任務f創建main通過ensure_future()呼叫。

要取消任務取消它最內側服務員(未來通過sleep()返回),而無需通過f1 -> f2 -> sleep鏈自上而下的打算,但明確取消sleep及以上的自下而上的順序鏈彈出。

這就是爲什麼你只能看到棧上的最後一個協程 - 其他所有的協程都會在異常展開時出現。

+0

那麼,單步跳過f2()的結尾將返回到f1,所以信息必須隱藏起來*某處* ... –

+0

我已經更新了我的答案。我希望我的解釋能夠闡明你的問題。 –

+0

嗯。所以顯然這一切都是透明地由C運行時處理的,而我從來沒有注意過任何它?因爲當我單步通過我的f2()的末尾時,我立即在f1()中結束。 (我相應地更新了這個問題。) –