python - Exception handled surprisingly in Pyside slots -
problem: when exceptions raised in slots, invoked signals, not seem propagate usual through pythons call stack. in example code below invoking:
on_raise_without_signal(): handle exception expected.on_raise_with_signal(): print exception , unexpectedly print success messageelseblock.
question: reason behind exception being handled surprisingly when raised in slot? implementation detail/limitation of pyside qt wrapping of signals/slots? there read in docs?
ps: came across topic when got surprising results upon using try/except/else/finally when implementing qabstracttablemodels virtual methods insertrows() , removerows().
# -*- coding: utf-8 -*- """testing exception handling in pyside slots.""" __future__ import unicode_literals, print_function, division import logging import sys pyside import qtcore pyside import qtgui logging.basicconfig(level=logging.debug) logger = logging.getlogger(__name__) class exceptiontestwidget(qtgui.qwidget): raise_exception = qtcore.signal() def __init__(self, *args, **kwargs): super(exceptiontestwidget, self).__init__(*args, **kwargs) self.raise_exception.connect(self.slot_raise_exception) layout = qtgui.qvboxlayout() self.setlayout(layout) # button invoke handler handles raised exception expected btn_raise_without_signal = qtgui.qpushbutton("raise without signal") btn_raise_without_signal.clicked.connect(self.on_raise_without_signal) layout.addwidget(btn_raise_without_signal) # button invoke handler handles raised exception via signal unexpectedly btn_raise_with_signal = qtgui.qpushbutton("raise signal") btn_raise_with_signal.clicked.connect(self.on_raise_with_signal) layout.addwidget(btn_raise_with_signal) def slot_raise_exception(self): raise valueerror("valueerror on purpose") def on_raise_without_signal(self): """call function raises exception directly.""" try: self.slot_raise_exception() except valueerror exception_instance: logger.error("{}".format(exception_instance)) else: logger.info("on_raise_without_signal() executed successfully") def on_raise_with_signal(self): """call slot raises exception via signal.""" try: self.raise_exception.emit() except valueerror exception_instance: logger.error("{}".format(exception_instance)) else: logger.info("on_raise_with_signal() executed successfully") if (__name__ == "__main__"): application = qtgui.qapplication(sys.argv) widget = exceptiontestwidget() widget.show() sys.exit(application.exec_())
as you've noted in question, real issue here treatment of unhandled exceptions raised in python code executed c++. not signals: affects reimplemented virtual methods well.
in pyside, pyqt4, , pyqt5 versions 5.5, default behaviour automatically catch error on c++ side , dump traceback stderr. normally, python script automatically terminate after this. not happens here. instead, pyside/pyqt script carries on regardless, , many people quite rightly regard bug (or @ least misfeature). in pyqt-5.5, behaviour has been changed qfatal() called on c++ side, , program abort normal python script would. (i don't know current situation pyside2, though).
so - should done this? best solution versions of pyside , pyqt install exception hook - because take precedence on default behaviour (whatever may be). unhandled exception raised signal, virtual method or other python code firstly invoke sys.excepthook, allowing customise behaviour in whatever way like.
in example script, mean adding this:
def excepthook(cls, exception, traceback): print('calling excepthook...') logger.error("{}".format(exception)) sys.excepthook = excepthook and exception raised on_raise_with_signal can handled in same way other unhandled exceptions.
of course, imply best practice pyside/pyqt applications use largely centralised exception handling. includes showing kind of crash-dialog user can report unexpected errors.
Comments
Post a Comment