This has cropped up in the course of some tests. I've tried to reproduce the problem in an MRE to isolate the problem but without success.
I have a test like this (simplified, but this simplified version manifests the same problem):
def test_if_index_already_exists_should_be_deleted(request, qtbot):
response = requests.delete(f'http://localhost:9200/some_index')
... adding these 2 lines and running pytest results in a series of unexpected fails and errors. I'm using pytest-random-order, so they vary. The errors are the dreaded
RuntimeError: wrapped C/C++ object of type QTextEdit has been deleted
... but if I comment out the requests.delete line nothing happens: all tests pass fine with multiple random order runs.
Whether or not the index exists shouldn't be an issue (or indeed whether Elasticsearch is running). I'm a bit baffled.
A QTextEdit is present in my GUI, and a signal is used to update it. The trace info from pytest looks like this:
File "/media/mike/software projects/EclipseWorkspace/doc_indexer/src/core/indexing_task_class.py", line 72, in set_extra_text
self.extra_info_text_edit.setText(msg)
RuntimeError: wrapped C/C++ object of type QTextEdit has been deleted
This shows that a signal to update this GUI element is being fired when these tests fail. In response I have removed the signal and all references to it in the app and test code. After this the tests all pass OK.
Why could calling a command requests.delete cause what seems to be a spurious signal firing (apparently during teardown)? Does pytest-qt use requests in its internals perhaps?
PS deleting the qtbot fixture above does not solve the problem.
Edit
If I put traceback.print_stack() in the slot which gets fired I get this:
...
File "/media/apps/Python/virtual_envs/doc_indexer/lib/python3.10/site-packages/pytestqt/plugin.py", line 142, in pytest_runtest_setup
_process_events()
File "/media/apps/Python/virtual_envs/doc_indexer/lib/python3.10/site-packages/pytestqt/plugin.py", line 182, in _process_events
app.processEvents()
File "/media/mike/software projects/EclipseWorkspace/doc_indexer/src/core/indexing_task_class.py", line 71, in set_extra_text
traceback.print_stack()
Exceptions caught in Qt event loop:
________________________________________________________________________________
Traceback (most recent call last):
File "/media/mike/software projects/EclipseWorkspace/doc_indexer/src/core/indexing_task_class.py", line 72, in set_extra_text
self.extra_info_text_edit.setText(msg)
RuntimeError: wrapped C/C++ object of type QTextEdit has been deleted
From the above it can be seen that the thing firing the signal is something inside pytestqt:
... /site-packages/pytestqt/plugin.py", line 182, in _process_events
That method looks like this:
def _process_events():
"""Calls app.processEvents() while taking care of capturing exceptions
or not based on the given item's configuration.
"""
app = qt_api.QtWidgets.QApplication.instance()
if app is not None:
app.processEvents()
I think... this sort of error is almost always caused by failing to include the pytest-qt
qtbotfixture when it needs to be included.From my experiments, this is quite an insidious error, in the sense that test suites can pass many times without any error occurring... and then suddenly one appears to occur without any real clue from any console (or logging) output about what test method is the issue.
In particular, I am using the moddule pytest-random-order, but I've also created a little script so I can do runs any number of times with a CLI switch. Typically therefore I may do the full test run 5 or so times so I can see when phenomena are occurring "intermittently". (In fact these turn out not to be intermittent: if I use the same
--random-order-seedvalue I get the same horrible error: but usingpytest-random-orderobviously runs each run with a different seed, and therefore runs the test files in random order, and all the tests within them in random order).The way I've had to identify this error is, then, by commenting out all my tests and gradually adding them back. More than once I've now found that it's a fairly new method which I added, without the
qtbotfixture, which turned out to be the culprit.