diff options
author | Florian Bruhin <me@the-compiler.org> | 2022-12-06 12:22:09 +0100 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2022-12-06 12:22:09 +0100 |
commit | 7b90fd37378853ed47153463298db66a3a98ecac (patch) | |
tree | f569d6b7175fb8be176872bc0033b62d24afe90d | |
parent | 3f5febf227d746ef9b9b1c90a0110c6e8c23cb3c (diff) | |
download | qutebrowser-7b90fd37378853ed47153463298db66a3a98ecac.tar.gz qutebrowser-7b90fd37378853ed47153463298db66a3a98ecac.zip |
Use a proper @pyqtSlot for FilenamePrompt
In bleeding tests, we started to get a segfault on the second test in
tests/unit/mainwindow/test_prompt.py, with a stacktrace like:
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
0x00007ffff7b55912 in _PyFunction_Vectorcall (func=<function at remote 0x7fffc4368ca0>, stack=0x7fffd74656a8, nargsf=<optimized out>, kwnames=0x0) at Objects/call.c:341
341 if (((PyCodeObject *)f->fc_code)->co_flags & CO_OPTIMIZED) {
(gdb) bt
#0 0x00007ffff7b55912 in _PyFunction_Vectorcall (func=<function at remote 0x7fffc4368ca0>, stack=0x7fffd74656a8, nargsf=<optimized out>, kwnames=0x0) at Objects/call.c:341
#1 0x00007ffff4e0b3f1 in PyQtSlot::call(_object*, _object*) const (this=0x555556c759c0, args=('/tmp/pytest-of-florian/pytest-70/test_simple_completion_1_next_0/test',), callable=<function at remote 0x7fffc4368ca0>)
at ../../qpy/QtCore/qpycore_pyqtslot.cpp:247
#2 PyQtSlot::invoke(void**, _object*, void*, bool) const (this=0x555556c759c0, qargs=<optimized out>, qargs@entry=0x7fffffff86c0, self=<optimized out>, self@entry=0x0, result=result@entry=0x0, no_receiver_check=<optimized out>)
at ../../qpy/QtCore/qpycore_pyqtslot.cpp:159
#3 0x00007ffff4e12213 in PyQtSlot::invoke(void**, bool) const (no_receiver_check=<optimized out>, qargs=0x7fffffff86c0, this=<optimized out>) at ../../qpy/QtCore/qpycore_pyqtslot.cpp:78
#4 PyQtSlotProxy::unislot(void**) (qargs=0x7fffffff86c0, this=0x555557193e20) at ../../qpy/QtCore/qpycore_pyqtslotproxy.cpp:205
#5 PyQtSlotProxy::unislot(void**) (qargs=0x7fffffff86c0, this=0x555557193e20) at ../../qpy/QtCore/qpycore_pyqtslotproxy.cpp:186
#6 PyQtSlotProxy::qt_metacall(QMetaObject::Call, int, void**) (this=0x555557193e20, _c=<optimized out>, _id=0, _a=0x7fffffff86c0) at ../../qpy/QtCore/qpycore_pyqtslotproxy.cpp:170
#7 0x00007ffff48bd91d in doActivate<false>(QObject*, int, void**) (sender=0x555556b3d680, signal_index=28, argv=0x7fffffff86c0) at kernel/qobject.cpp:3945
#8 0x00007fffeff96aca in QFileSystemModel::directoryLoaded(QString const&) (this=<optimized out>, _t1=<optimized out>) at .moc/moc_qfilesystemmodel.cpp:272
#9 0x00007ffff48b0be0 in QObject::event(QEvent*) (this=this@entry=0x555556b3d680, e=e@entry=0x7fff0c004af0) at kernel/qobject.cpp:1347
#10 0x00007fffeff962ab in QFileSystemModel::event(QEvent*) (this=this@entry=0x555556b3d680, event=event@entry=0x7fff0c004af0) at dialogs/qfilesystemmodel.cpp:1748
#11 0x00007ffff070995c in sipQFileSystemModel::event(QEvent*) (this=0x555556b3d680, a0=0x7fff0c004af0) at /usr/src/debug/pyqt5/PyQt5-5.15.7/build/QtWidgets/sipQtWidgetsQFileSystemModel.cpp:376
[...]
#23 0x00007ffff4da94ce in meth_QCoreApplication_processEvents(PyObject*, PyObject*, PyObject*) (sipArgs=<optimized out>, sipKwds=0x0) at /usr/src/debug/pyqt5/PyQt5-5.15.7/build/QtCore/sipQtCoreQCoreApplication.cpp:590
[...]
from pytest-qt in Python:
Current thread 0x00007fb366207740 (most recent call first):
File ".../pytestqt/plugin.py", line 209 in _process_events
File ".../pytestqt/plugin.py", line 171 in pytest_runtest_setup
[...]
File ".../pytest/src/_pytest/runner.py", line 115 in pytest_runtest_protocol
[...]
File ".../pytest/src/_pytest/main.py", line 317 in pytest_cmdline_main
[...]
Introduced by this change in pytest, which now deletes the temporary
directory after the test by default:
https://github.com/pytest-dev/pytest/pull/10517
It sounds like something odd like the directory removal causing (Py)Qt
to call the lambda, but the underlying Python object already being gone
or something along those lines. With a proper Qt slot (@pyqtSlot),
PyQt seems to do the right thing, so let's just use that instead...
-rw-r--r-- | qutebrowser/mainwindow/prompt.py | 18 |
1 files changed, 15 insertions, 3 deletions
diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 5d3bced59..c5acaabda 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -760,9 +760,21 @@ class FilenamePrompt(_BasePrompt): self._file_view.setColumnHidden(col, True) # Nothing selected initially self._file_view.setCurrentIndex(QModelIndex()) - # The model needs to be sorted so we get the correct first/last index - self._file_model.directoryLoaded.connect( - lambda: self._file_model.sort(0)) + + self._file_model.directoryLoaded.connect(self.on_directory_loaded) + + @pyqtSlot() + def on_directory_loaded(self): + """Sort the model after a directory gets loaded. + + The model needs to be sorted so we get the correct first/last index. + + NOTE: This needs to be a proper @pystSlot() function, and not a lambda. + Otherwise, PyQt seems to fail to disconnect it immediately after the + object gets destroyed, and we get segfaults when deleting the directory + in unit tests. + """ + self._file_model.sort(0) def accept(self, value=None, save=False): self._check_save_support(save) |