Here\'s the code that illustrates the problem:
from PyQt4 import QtGui
app = QtGui.QApplication([])
dialog = QtGui.QDialog()
button = QtGui.QPushButton(\'I c
I think the answer is that this isn't a 'feature' of PyQt
, but a consequence inherent to the design that lets signals/slots work (remember that the signal/slot communication is going through a c++ layer as well).
This is ugly, but does a bit of and end-run around your problem
from PyQt4 import QtGui
import time
app = QtGui.QApplication([])
class exception_munger(object):
def __init__(self):
self.flag = True
self.txt = ''
self.type = None
def indicate_fail(self,etype=None, txt=None):
self.flag = False
if txt is not None:
self.txt = txt
self.type = etype
def reset(self):
tmp_txt = self.txt
tmp_type = self.type
tmp_flag = self.flag
self.flag = True
self.txt = ''
self.type = None
return tmp_flag, tmp_type, tmp_txt
class e_manager():
def __init__(self):
self.old_hook = None
def __enter__(self):
em = exception_munger()
def my_hook(type, value, tback):
em.indicate_fail(type, value)
sys.__excepthook__(type, value, tback)
self.old_hook = sys.excepthook
sys.excepthook = my_hook
self.em = em
return self
def __exit__(self,*args,**kwargs):
sys.excepthook = self.old_hook
def mang_fac():
return e_manager()
def assert_dec(original_fun):
def new_fun(*args,**kwargs):
with mang_fac() as mf:
res = original_fun(*args, **kwargs)
flag, etype, txt = mf.em.reset()
if not flag:
raise etype(txt)
return res
return new_fun
@assert_dec
def my_test_fun():
dialog = QtGui.QDialog()
button = QtGui.QPushButton('I crash')
layout = QtGui.QHBoxLayout()
layout.addWidget(button)
dialog.setLayout(layout)
def crash():
time.sleep(1)
raise Exception('Crash!')
button.clicked.connect(crash)
button.click()
my_test_fun()
print 'should not happen'
This will not print 'should not happen' and gives you something to catch with your automated tests (with the correct exception type).
In [11]: Traceback (most recent call last):
File "/tmp/ipython2-3426rwB.py", line 68, in crash
Exception: Crash!
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-11-6ef4090ab3de> in <module>()
----> 1 execfile(r'/tmp/ipython2-3426rwB.py') # PYTHON-MODE
/tmp/ipython2-3426rwB.py in <module>()
/tmp/ipython2-3426rwB.py in new_fun(*args, **kwargs)
Exception: Crash!
In [12]:
The stack trace is jacked up, but you can still read the first one that was printed out.
This is not the answer, you silly website. Stop forcing us to fit into a preconceived notion of an idealized thread template.
The un-answer is to use my test framework setUp() to hook in an exception handler:
def setUp(self):
self.no_exceptions = True
def testExceptionHook(type, value, tback):
self.no_exceptions = False
sys.__excepthook__(type, value, tback)
sys.excepthook = testExceptionHook
Where that says "self.no_exceptions = False", I would much rather simply say self.fail(''). However, because Python's unit test library insists on throwing exceptions just to register test failures, and because PyQt insists on snarfing all exceptions, we have a deadlock.
To fit into unittest.TestCase's silly preconceived notion of an idealized test case, I have to instead set a variable, then detect it in the teardown:
def tearDown(self):
self.assertTrue(self.no_exceptions)
This is still not ideal, but at least it will force me to spend more time paying attention to the errors, instead of spending that time complaining about them on technical websites.
The root question: How to turn off PyQt's magic error handler? - remains unanswered...