2016-09-23 88 views
1

提高了exception我想跳進那個框架。爲了更好地解釋我的意思是我寫這篇兆瓦:如何從異常「跳」到堆棧幀?

假設我有以下代碼:

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) 

我們調試,我想這個問題「跳轉」到最初引發exceptionZeroDivisionError)的幀中。

最初的例外仍然可以在self._value下完成self._value.__traceback__

回答

1

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') 

如果需要的話,不斷重複,直到你得到所期望的目標上下文/回溯。


現在的多重情況下,我不知道會作出這種太大的差別,因爲這個問題意味着一般的東西(如何從異常「跳」到的StackFrame?),但事實證明,在multiprocessing的細節做了所有的差異。

在Python 3.4中,一個解決方法是將traceback顯示爲一個字符串;由於回溯實際上有多少東西,所以在Python跟蹤器上的issue 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__ 

所以,直到他們弄清楚如何使跨進程的所有國家這實際上不太可能。

+0

謝謝你的回答。我用我的例子嘗試過,但是'self._value .__ context__'是'None'。這是由於這樣一個事實,即異常被提出而不是重新加註(不加引發地提出)? –

+0

哦,嗯,遠程回溯看起來像是一個字符串,但是考慮到你得到了什麼(例外是'self._value'),你可能想用'self._value .__ traceback__'來完成。對不起,我的答案沒有更清楚。 – metatoaster

+0

我試過了。然而它在主進程中用'raise self._value'加載框架。我想這是由於多處理造成的限制。我絕對從你的答案中學到了一些東西,並會接受它,除非有人在第二天找到解決問題的辦法。 –