[prev in list] [next in list] [prev in thread] [next in thread]
List: pykde
Subject: Re: [PyQt] Automatically avoiding segfaults with sip.setdeleted()?
From: Phil Thompson <phil () riverbankcomputing ! com>
Date: 2017-04-19 21:41:12
Message-ID: 0A647467-CD30-4512-A50C-4579EA2393A1 () riverbankcomputing ! com
[Download RAW message or body]
On 19 Apr 2017, at 9:30 pm, Russell Warren <russ@perspexis.com> wrote:
>
> I've been looking into frustratingly frequent segfaults with a PyQt5 application \
> today. The go-to reason is usually that some C-side Qt instance has been deleted \
> without PyQt being aware of it. PyQt is decent at handling this case gracefully \
> sometimes (with a nice "RuntimeError: wrapped C/C++ object of type <whatever> has \
> been deleted" exception), but it is definitely not perfect.
> Experimentation seems to show that when python is aware of the hierarchy/parentage \
> via python-side constructors, it is handled well, as in this short example:
> > > > from PyQt5 import QtCore
> > > > obj1 = QtCore.QObject()
> > > > obj2 = QtCore.QObject(parent = obj1)
> > > > del obj1 # <-- Qt will also delete the obj2 child
> > > > obj2.objectName()
> Traceback (most recent call last):
> File "<input>", line 1, in <module>
> obj2.objectName()
> RuntimeError: wrapped C/C++ object of type QObject has been deleted
>
> This exception is great. However, when the parentage is set internally to the C++ \
> code (without python knowing about it), segfaults can result. For example:
> > > > from PyQt5 import QtWidgets
> > > > app = QtWidgets.QApplication([])
> > > > w = QtWidgets.QMainWindow()
> > > > sb = w.statusBar() # <-- Qt internals set the child relationship
> > > > del w # <-- Qt deletes sb child w/o python knowing
> > > > sb.objectName()Segmentation fault
Parentage isn't the issue. It's the fact that the C++ instances are being created \
internally and not by your application. The workaround is to not keep long-lived \
references to internally generated objects - which is bad practice anyway IMHO.
> That segfault is clearly undesirable. However... fixing it is conceptually trivial, \
> since you just need to hook sip.setdeleted() up to the 'destroyed' signal:
> > > > import sip
> > > > from PyQt5 import QtWidgets
> > > > def on_destroy(obj):
> ... sip.setdeleted(obj)
> ...
> > > > app = QtWidgets.QApplication([])
> > > > w = QtWidgets.QMainWindow()
> > > > sb = w.statusBar()
> > > > sb.destroyed.connect(on_destroy)
> > > > del w
> > > > sb.objectName()
> Traceback (most recent call last):
> File "<input>", line 1, in <module>
> sb.objectName()
> RuntimeError: wrapped C/C++ object of type QStatusBar has been deleted
> > > > # rejoice! no segfault!
>
> Getting that RuntimeError is clearly MUCH better than getting a segfault. but \
> having to manually connect/manage all of those 'destroyed' connections in \
> application code is obviously not ideal, and is prone to errors.
> It seems like a good solution would be to ALWAYS connect the QObject.destroyed \
> signal of wrapped QObject instances to sip.setdeleted.
> Is there a way to do this destroyed->sip.setdeleted hookup automatically for all \
> wrapped QObjects without needing to manage it for each PyQt5.QObject instance at \
> the application layer?
Experience has shown that this creates as many problems as it solves when applied \
generally. The approach would be useful to apply in specific cases (ie. with C++ \
instances that are known to have been created internally) but SIP doesn't have the \
ability to detect those cases. I'll add it to the TODO list.
Phil
_______________________________________________
PyQt mailing list PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic