[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