为什么sys.excepthook在包装时表现不同?

时间:2018-03-02 08:33:28

标签: python pyqt

Qt静默捕获Python回调中的异常,并使用错误代码退出程序。这可以通过一个简短的例子来证明:

import sys
from PyQt5 import QtWidgets 

# _excepthook = sys.excepthook
# def exception_hook(exctype, value, traceback):
#     _excepthook(exctype, value, traceback)
# sys.excepthook = exception_hook

class Test(QtWidgets.QPushButton):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setText("hello")
        self.clicked.connect(self.buttonClicked)

    def buttonClicked(self):
        print("clicked")
        raise Exception("wow")

app = QtWidgets.QApplication(sys.argv)
t = Test()
t.show()
app.exec_()

点击按钮后,我们

  

点击

     

使用退出代码1完成处理

This answer(我修改了该示例)演示了如何安装自定义异常挂钩。因此,请在上面的示例中取消注释代码行。现在它打印回溯,并且每次单击按钮时都不会退出程序。

自定义函数只是旧函数的一个薄包装器。为什么在引发异常时会导致不同的行为?

1 个答案:

答案 0 :(得分:4)

在PyQt4和旧版本的PyQt5(5.4或更早版本)中,行为是在您描述的任何情况下永远不会退出应用程序。这在PyQt 5.5+中已更改(导致应用程序退出),但前提是没有为sys.excepthook显式指定的异常处理程序。这在[{3}}中有所提及,但在documentation上有更详细的说明。

文件中的相关部分:

  

在许多情况下执行Python代码   C ++。 Python重新实现C ++虚拟方法可能就是这样   最常见的例子。在以前的版本中,如果引发了Python代码   PyQt会调用Python的PyErr_Print()函数   然后调用sys.excepthook()。默认的异常挂钩   然后会显示异常和任何追溯到stderr。那里   这种行为有多少缺点:

     
      
  • 应用程序不会终止,这意味着行为不同   当在其他情况下引发例外时
  •   
  • 开发人员或用户可能看不到写入stderr的输出(特别是如果它   是一个GUI应用程序)从而隐藏了应用程序的事实   试图报告潜在的错误。
  •   
     

此行为已被弃用   PyQt v5.4。在PyQt v5.5中,未处理的Python异常将导致a   调用Qt的qFatal()函数。默认情况下,这将调用abort()和   申请将终止。请注意,已安装应用程序   异常钩子仍然优先。

邮件列表主题中的相关部分:

  
    

我刚刚发现对PyQt 5.5的更改,其中未处理的异常     导致调用qFatal()。也许我错过了一些重要的东西,但是     我很困惑为什么选择这种行为。文档     指出旧行为的问题在于应用程序的问题     不终止,意味着行为与异常时不同     在其他情况下提出"。我对这个推理有两个担忧:

  
     

因为当你正在运行时,你无法干净地退出Python   C ++代码。

     
    
        
  1. Python中未处理的异常不会导致程序终止;     它们只会导致调用sys.excepthook。
  2.        
     

与PyQt相同,如果你设置了一个。

也许值得指出的是,pyqtgraph的创建者在pyqt邮件列表上提出了原始问题,并且河岸计算的工作人员说这种新行为并没有消失。

如果你想转到源代码,相关代码位于pyqt5 / qpy / QtCore / qpycore_public_api.cpp(PyQt5的分叉版本为mailing list