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

问题描述 投票:4回答:1

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

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

python pyqt
1个回答
4
投票

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

文件中的相关部分:

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

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

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

邮件列表线程的相关部分:

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

因为当前运行C ++代码时无法干净地退出Python。

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.