sys.exc_info()是如何工作的?

5

sys.exc_info()方法的行为在Python文档SO以及SO中描述如下:

  • 在except块或由except块调用的方法内,返回一个描述异常的三元组;
  • 在其他地方,返回三个None值的三元组。

那么为什么这个nosetest会失败呢?

def test_lang_stack(self):
    try:
        self.assertEquals((None,None,None), sys.exc_info()) # no exception
        a = 5 / 0
    except:
        self.assertNotEquals((None,None,None), sys.exc_info())  # got exception
    else:
        self.fail('should not get here')
    #finally:
    #    self.assertEquals((None,None,None), sys.exc_info()) # already handled, right?
    self.assertEquals((None,None,None), sys.exc_info())  # already handled, right?

最后一行出错了。如果取消finally块的注释,则会在那里出错。

我发现,如果我将所有这些内容放在一个方法内,并从另一个方法调用,则调用方法看不到异常。即使在处理异常之后,exc_info值似乎仍然设置为抛出异常的方法的结尾。

我正在使用Mac OSX上的python2.7。

2个回答

5
在回答您的问题之前,我必须解释一下sys.exc_info()是如何确定程序中最新/最近异常的:
您的程序基本上是一系列的函数调用,调用者函数会调用被调用的函数。因此,形成了一个调用栈/执行栈,在每个被调用的函数中都会推送一项入口。在python中,这个条目称为堆栈帧(stack frame)。所以,当在您的程序中调用sys.exc_info()时,它使用以下顺序来确定最新的异常。它从当前堆栈帧开始,即从您调用sys.exc_info()的函数开始。如果当前堆栈帧没有处理/未处理异常,那么信息将从调用堆栈帧或其调用者中获取,以此类推,直到找到正在处理/已处理异常的堆栈帧。在这里,“处理异常”定义为“执行或已执行except子句”。对于任何堆栈帧,只能访问有关最近处理的异常的信息。
现在,进入您的代码。
def test_lang_stack(self):
    try:
        self.assertEquals((None,None,None), sys.exc_info()) # no exception
        a = 5 / 0
    except:
        self.assertNotEquals((None,None,None), sys.exc_info())  # got exception
    else:
        self.fail('should not get here')
    #finally:
    #    self.assertEquals((None,None,None), sys.exc_info()) # already handled, right?
    self.assertEquals((None,None,None), sys.exc_info())  # already handled, right?

根据上述解释,sys.exc_info()总是会找到你的函数中正在处理的最近的异常。因此,它永远不会是一个由Nones组成的元组,除非你明确调用sys.exec_clear()。
希望这能对你有所帮助。

2

在assertEquals()和assertNotEquals()方法中,你需要调用:

sys.exc_clear()

这将为下一个错误清理事物。


我可以手动清除错误,但关键是exc_info()调用应区分我处理异常的上下文(在except:块中)和未处理该异常的上下文(其他任何地方)。我不想在我们的整个项目中每个try/except语句后都显式添加exc_clear()! - user634943
我认为设计的思路是只在异常块中使用sys.exc_info()。 - cforbish
我同意@user634943的看法,如果在except块之外调用exc_info(),则最好返回(None,None,None)的方法将是很好的。不幸的是,这不会自动发生,目前我们确实需要手动调用exc_clear()... - snooze92
或者@cforbish,有没有一种方法可以让Python询问一段代码是否在except块中? - snooze92

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接