<p>如果您想让最终用户看到您从未调用过<code>g()</code>,那么您需要存储第一个错误的回溯,调用第二个函数,然后将原始回溯与原始回溯一起抛出。(否则,在Python2中,bare raise会重新引发第二个异常,而不是第一个异常)。问题是没有2/3兼容的方法来引发回溯,所以您必须用<code>exec</code>语句包装Python2版本(因为它在Python3中是<code>SyntaxError</code>)。在</p>
<p>这里有一个函数可以让您这样做(我最近将它添加到<code>pandas</code>代码库中):</p>
<pre><code>import sys
if sys.version_info[0] >= 3:
def raise_with_traceback(exc, traceback=Ellipsis):
if traceback == Ellipsis:
_, _, traceback = sys.exc_info()
raise exc.with_traceback(traceback)
else:
# this version of raise is a syntax error in Python 3
exec("""
def raise_with_traceback(exc, traceback=Ellipsis):
if traceback == Ellipsis:
_, _, traceback = sys.exc_info()
raise exc, None, traceback
""")
raise_with_traceback.__doc__ = (
"""Raise exception with existing traceback.
If traceback is not passed, uses sys.exc_info() to get traceback."""
)
</code></pre>
<p>然后您可以这样使用它(为了清晰起见,我还更改了异常类型)。在</p>
^{pr2}$
<p>在Python2中,您只看到<code>a()</code>和{<cd6>}:</p>
<pre><code>Traceback (most recent call last):
File "test.py", line 40, in <module>
raise_with_traceback(e, tb)
File "test.py", line 31, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
</code></pre>
<p>但在python3中,它仍然注意到还有一个额外的异常,因为您在它的<code>except</code>子句中引发了这个异常[它颠倒了错误的顺序,并使用户更加困惑]:</p>
<pre><code>Traceback (most recent call last):
File "test.py", line 38, in <module>
g()
File "test.py", line 25, in g
raise ValueError("coconut")
ValueError: coconut
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 40, in <module>
raise_with_traceback(e, tb)
File "test.py", line 6, in raise_with_traceback
raise exc.with_traceback(traceback)
File "test.py", line 31, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
</code></pre>
<p>如果您绝对希望它看起来像是在Python 2和Python 3中从未发生过<code>g()</code>异常,那么您需要首先检查您是否退出了<code>except</code>子句:</p>
<pre><code>try:
a()
except TypeError as e:
import sys
# save the traceback from the original exception
_, _, tb = sys.exc_info()
handled = False
try:
# attempt witty workaround
g()
handled = True
except:
pass
if not handled:
raise_with_traceback(e, tb)
</code></pre>
<p>这将在Python 2中实现以下回溯:</p>
<pre><code>Traceback (most recent call last):
File "test.py", line 56, in <module>
raise_with_traceback(e, tb)
File "test.py", line 43, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
</code></pre>
<p>Python3中的回溯:</p>
<pre><code>Traceback (most recent call last):
File "test.py", line 56, in <module>
raise_with_traceback(e, tb)
File "test.py", line 6, in raise_with_traceback
raise exc.with_traceback(traceback)
File "test.py", line 43, in <module>
a()
File "test.py", line 28, in a
f()
File "test.py", line 22, in f
raise TypeError("sparrow")
TypeError: sparrow
</code></pre>
<p>它确实添加了一个额外的无用的回溯行,向用户显示<code>raise exc.with_traceback(traceback)</code>,但相对来说是干净的。在</p>