【问题标题】:Why does sys.excepthook behave differently when wrapped?为什么 sys.excepthook 在包装时表现不同?
【发布时间】:2018-03-02 19:41:55
【问题描述】:

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

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

【问题讨论】:

  • 你能edit 显示完整的minimal reproducible example 并安装了异常挂钩吗?
  • @Jean-FrançoisFabre 我不喜欢用示例代码的冗余副本浪费空间,但我同意指出插入 sn-p 的确切位置是有意义的(尽管没有很多地方合理的地方)。我希望我找到的权衡对我们所有人都有效。

标签: python pyqt


【解决方案1】:

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

文档中的相关部分:

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

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

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

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

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

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

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

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

也许还值得指出的是,最初的问题是由 pyqtgraph 的创建者在 pyqt 邮件列表中提出的,并且河岸计算的工作人员表示这种新行为不会消失。

如果要去看源码,相关代码位于pyqt5/qpy/QtCore/qpycore_public_api.cpp(PyQt5的一个fork版本是here

【讨论】:

  • 好收获! RTFM 再次获胜 :) 您介意在答案中直接引用文档的相关部分吗?我的链接有问题,只能看到一个奇怪的缓存版本。
  • @kazemakase 我也遇到了麻烦,这就是为什么我还没有引用它的原因(我依靠回程机器来回答这个问题)。明天文档备份后,我会添加一些相关的引用!
  • @Jean-FrançoisFabre 答案更新更全面
猜你喜欢
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
  • 2015-12-09
  • 2022-11-27
  • 2015-05-09
  • 1970-01-01
  • 2016-01-01
相关资源
最近更新 更多