簡體   English   中英

為什么在包裝時sys.excepthook會有不同的行為?

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

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完成

這個答案 (我修改了該示例)顯示了如何安裝自定義異常掛鈎。 因此,讓我們取消上面示例中的代碼行注釋。 現在,它打印回溯,並且每次我們單擊該按鈕時都不會退出程序。

自定義函數只是舊函數的一個薄包裝。 為什么在引發異常時會導致不同的行為?

在PyQt4和舊版本的PyQt5(5.4或更舊版本)中,行為是在您描述的任何情況下都永遠不會退出應用程序。 在PyQt 5.5+中對此進行了更改(導致應用程序退出),但sys.excepthook是沒有為sys.excepthook明確指定異常處理程序。 這在文檔中有所提及,但在郵件列表中也有更詳細的說明。

文檔中的相關部分:

在很多情況下,都可以從C ++執行Python代碼。 C ++虛擬方法的Python重新實現可能是最常見的示例。 在以前的版本中,如果Python代碼引發異常,則PyQt將調用Python的PyErr_Print()函數,然后該函數將調用sys.excepthook()。 然后,默認的異常掛鈎將顯示異常以及對stderr的任何回溯。 這種行為有很多缺點:

  • 應用程序不會終止,這意味着行為與在其他情況下引發異常時不同
  • 開發人員或用戶(尤其是GUI應用程序)可能看不到寫入stderr的輸出,從而隱藏了該應用程序試圖報告潛在錯誤的事實。

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的分叉版本)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM