2016-09-23 56 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'加载框架。我想这是由于多处理造成的限制。我绝对从你的答案中学到了一些东西,并会接受它,除非有人在第二天找到解决问题的办法。 –