简体   繁体   English

为什么在包装时sys.excepthook会有不同的行为?

[英]Why does sys.excepthook behave differently when wrapped?

Qt silently catches exceptions in Python callbacks and exits the program with an error code. Qt静默捕获Python回调中的异常,并以错误代码退出程序。 This can be demonstrated with a short example: 这可以用一个简短的例子来证明:

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_()

When clicking the button we get 点击按钮,我们得到

clicked 点击了

Process finished with exit code 1 流程以退出代码1完成

This answer (from which I modified the example) shows how to install a custom exception hook. 这个答案 (我修改了该示例)显示了如何安装自定义异常挂钩。 So lets uncomment the code lines in the example above. 因此,让我们取消上面示例中的代码行注释。 Now it prints the traceback and does not exit the program every time we click the button. 现在,它打印回溯,并且每次我们单击该按钮时都不会退出程序。

The custom function is just a thin wrapper of the old function. 自定义函数只是旧函数的一个薄包装。 Why does this cause different behavior when an exception is raised? 为什么在引发异常时会导致不同的行为?

In PyQt4 and old versions of PyQt5 (5.4 or older) the behaviour was to never exit the application in any of the situations you describe. 在PyQt4和旧版本的PyQt5(5.4或更旧版本)中,行为是在您描述的任何情况下都永远不会退出应用程序。 This was changed in PyQt 5.5+ (to cause the application to exit) but only if there is no exception handler explicitly specified for sys.excepthook . 在PyQt 5.5+中对此进行了更改(导致应用程序退出),但sys.excepthook是没有为sys.excepthook明确指定异常处理程序。 This is somewhat mentioned in the documentation but also in more detail on the mailing list . 这在文档中有所提及,但在邮件列表中也有更详细的说明。

The relevant part from the documentation: 文档中的相关部分:

There are a number of situations where Python code is executed from C++. 在很多情况下,都可以从C ++执行Python代码。 Python reimplementations of C++ virtual methods is probably the most common example. C ++虚拟方法的Python重新实现可能是最常见的示例。 In previous versions, if the Python code raised an exception then PyQt would call Python's PyErr_Print() function which would then call sys.excepthook(). 在以前的版本中,如果Python代码引发异常,则PyQt将调用Python的PyErr_Print()函数,然后该函数将调用sys.excepthook()。 The default exception hook would then display the exception and any traceback to stderr. 然后,默认的异常挂钩将显示异常以及对stderr的任何回溯。 There are number of disadvantages to this behaviour: 这种行为有很多缺点:

  • the application does not terminate, meaning the behaviour is different to when exceptions are raised in other situations 应用程序不会终止,这意味着行为与在其他情况下引发异常时不同
  • the output written to stderr may not be seen by the developer or user (particularly if it is a GUI application) thereby hiding the fact that the application is trying to report a potential bug. 开发人员或用户(尤其是GUI应用程序)可能看不到写入stderr的输出,从而隐藏了该应用程序试图报告潜在错误的事实。

This behaviour was deprecated in PyQt v5.4. PyQt v5.4中已弃用此行为。 In PyQt v5.5 an unhandled Python exception will result in a call to Qt's qFatal() function. 在PyQt v5.5中,未处理的Python异常将导致调用Qt的qFatal()函数。 By default this will call abort() and the application will terminate. 默认情况下,它将调用abort(),应用程序将终止。 Note that an application installed exception hook will still take precedence. 请注意,应用程序安装的异常挂钩仍将优先。

The relavant part from the mailing list thread: 来自邮件列表线程的相关部分:

I have just discovered the change to PyQt 5.5 in which unhandled exceptions result in a call to qFatal(). 我刚刚发现了PyQt 5.5的更改,其中未处理的异常导致调用qFatal()。 Perhaps I am missing something important, but I am a confused about why this behavior was chosen. 也许我错过了一些重要的事情,但是我对为什么选择这种行为感到困惑。 The documentation states that the problem with the old behavior is that "the application does not terminate, meaning the behaviour is different to when exceptions are raised in other situations". 该文档指出,旧行为的问题在于“应用程序不会终止,这意味着行为与在其他情况下引发异常时不同”。 I have two concerns about this reasoning: 我对此有两个担忧:

Because you can't cleanly exit Python when you're currently running C++ code. 因为您当前正在运行C ++代码时无法彻底退出Python。

  1. Unhandled exceptions in Python do not cause the program to terminate; Python中未处理的异常不会导致程序终止。 they only cause sys.excepthook to be invoked. 它们只会导致sys.excepthook被调用。

Same with PyQt, if you set one. 如果您设置了一个,则与PyQt相同。

It is perhaps also worth pointing out that the original question was raised on the pyqt mailing list by the creator of pyqtgraph and that staff at riverbank computing have said this new behaviour is not going away. 也许值得指出的是,最初的问题是pyqtgraph的创建者在pyqt邮件列表上提出的,并且河岸计算人员表示这种新行为不会消失。

If you want to go to the source code, the relevant code is located in pyqt5/qpy/QtCore/qpycore_public_api.cpp (a forked version of PyQt5 is here ) 如果要转到源代码,相关代码位于pyqt5 / qpy / QtCore / qpycore_public_api.cpp( 此处是PyQt5的分叉版本)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM