簡體   English   中英

如何從異常“跳轉”到堆棧幀?

[英]How can I "jump" into stackframe from exception?

有一個引發的exception我想跳入該框架。 為了更好地解釋我的意思,我寫了這個 mwe:

假設我有以下代碼:

from multiprocessing import Pool
import sys

# Setup debugger
def raiseDebugger(*args):
    """ http://code.activestate.com/recipes/65287-automatically-start-the-
    debugger-on-an-exception/ """

    import traceback, pdb
    traceback.print_exception(*args)
    pdb.pm()

sys.excepthook = raiseDebugger


# Now start with the question

def faulty(i):
    return 1 / i


with Pool() as pool:
    pool.map(faulty, range(6))

不出所料,這會導致:

multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "/home/bin/conda/lib/python3.5/multiprocessing/pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
  File "/home/bin/conda/lib/python3.5/multiprocessing/pool.py", line 44, in mapstar
    return list(map(*args))
  File "test2.py", line 19, in faulty
    return 1 / i
ZeroDivisionError: division by zero
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test2.py", line 23, in <module>
    pool.map(faulty, range(6))
  File "/home/bin/conda/lib/python3.5/multiprocessing/pool.py", line 260, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/home/bin/conda/lib/python3.5/multiprocessing/pool.py", line 608, in get
    raise self._value
ZeroDivisionError: division by zero
> /home/bin/conda/lib/python3.5/multiprocessing/pool.py(608)get()
-> raise self._value
(Pdb)

現在要調試問題,我想“跳”到最初引發exception的框架( ZeroDivisionError )。

原始異常在self._value下仍然可用self._value.__traceback__完成。

呼叫pm (或post_mortem )調用是從價值領域sys.exc_info ,並默認調用post_mortem是在做__traceback__該值的。 但是,如果您想訪問底層對象,則需要訪問其__context__ 鑒於此代碼示例:

import pdb
import sys
import traceback

def top():
    value = 1
    raise Exception('this always fails')

def bottom():
    try:
        top()
    except Exception as bot_ex:
        x = {}
        return x['nothing']

try:
    bottom()
except Exception as main_ex:
    pdb.post_mortem()

運行代碼。 main_ex將類似於您的self._value

> /tmp/foo.py(14)bottom()
-> return x['nothing']
(Pdb) main_ex
KeyError('nothing',)
(Pdb) pdb.post_mortem(main_ex.__traceback__)
> /tmp/foo.py(14)bottom()
-> return x['nothing']

請注意,我們在同一位置有一個新的 pdb 提示,這是最初引發異常的地方。 如果我們需要更進一步,讓我們用__context__嘗試一下:

(Pdb) c
(Pdb) pdb.post_mortem(main_ex.__context__.__traceback__)
> /tmp/foo.py(7)top()
-> raise Exception('this always fails')

如果需要,請繼續重復,直到到達所需的目標上下文/回溯。


現在對於多處理情況,我不知道會產生如此大的差異,因為這個問題暗示了一些一般性的東西(如何從異常“跳轉”到堆棧幀?),但事實證明, multiprocessing的細節使所有的區別。

在 Python 3.4 中,一種解決方法是將回溯顯示為字符串; 由於回溯實際上有多少內容,如 Python 跟蹤器上的問題 13831 中所討論的那樣,傳達所有被證明是困難的,因此進行了黑客攻擊以將__cause__屬性帶入當前異常,但它不是完整的__traceback__因為它只有字符串表示,正如我所懷疑的那樣。

無論如何,這就是會發生的事情:

(Pdb) !import pdb
(Pdb) !self._value.__cause__
RemoteTraceback('\n"""\nTraceback (most recent call last):...',)
(Pdb) !type(self._value.__cause__)
<class 'multiprocessing.pool.RemoteTraceback'>
(Pdb) !self._value.__cause__.__traceback__
(Pdb) !self._value.__cause__.__context__

所以這實際上是不可能的,除非他們弄清楚如何跨越流程邊界使所有這些狀態。

暫無
暫無

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

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