Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unhandled exceptions in PyQt5

Have a look at the following MWE.

import sys

from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.button = QPushButton('Bham!')
        self.setCentralWidget(self.button)
        self.button.clicked.connect(self.btnClicked)

    def btnClicked(self):
        print(sys.excepthook)
        raise Exception


#import traceback
#sys.excepthook = traceback.print_exception

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    app.exec_() 

I have a number of questions. I don't know if they are all related (I guess so), so forgive me if they are not.

  1. When I run the above code from the terminal, all is fine. The program runs, if I click the button it prints the traceback and dies. If I run it inside an IDE (I tested Spyder and PyCharm), the traceback is not displayed. Any idea why? Essentially the same question was raised in other posts also on SO, here and here. Please don't mark this as a duplicate of either of those; please read on.

  2. By adding the commented lines, the traceback is again displayed properly. However, they also have the nasty side effect that the app does no longer terminate on unhandled exceptions! I have no idea why this happens, as AFAIK excepthook only prints the traceback, it cannot prevent the program from exiting. At the moment it is called, it's too late for rescue.

  3. Also, I don't understand how Qt comes into play here, as exceptions that are not thrown inside a slot still crash the app as I would expect. No matter if I change excepthook or not, PyQt does not seem to override it as well (at least the print seems to suggest so).

FYI, I am using Python 3.5 with PyQt 5.6, and I am aware of the changes in the exception handling introduced in PyQt 5.5. If those are indeed the cause for the behaviour above, I would be glad hear some more detailed explanations.

like image 869
polwel Avatar asked Apr 26 '17 18:04

polwel


1 Answers

When an exception happens inside a Qt slot, it's C++ which called into your Python code. Since Qt/C++ know nothing about Python exceptions, you only have two possibilities:

  • Print the exception and return some default value to C++ (like 0, "" or NULL), possibly with unintended side effects. This is what PyQt < 5.5 does.
  • Print the exception and then call qFatal() or abort(), causing the application to immediately exit inside C++. That's what PyQt >= 5.5 does, except when you have a custom excepthook set.

The reason Python still doesn't terminate is probably because it can't, as it's inside some C++ code. The reason your IDE isn't showing the stack is probably because it doesn't deal with the abort() correctly - I'd suggest opening a bug against the IDE for that.

like image 107
The Compiler Avatar answered Nov 08 '22 17:11

The Compiler