<div dir="ltr">Phil,<div><br></div><div>I'm always impressed with you quick turnaround. Thanks for the fix!</div><div><br></div><div>Bryan</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Feb 10, 2016 at 4:38 PM, Phil Thompson <span dir="ltr"><<a href="mailto:phil@riverbankcomputing.com" target="_blank">phil@riverbankcomputing.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5"><br>
> On 10 Feb 2016, at 8:57 pm, Jones, Bryan <<a href="mailto:bjones@ece.msstate.edu">bjones@ece.msstate.edu</a>> wrote:<br>
><br>
> Thanks for the feedback. The first two crashes (quickCrash and slowCrash) were, as you say, fairly obvious programmer errors. It would be nice if this would emit some sort of error ("QApplication destroyed; terminating application") with a backtrace.<br>
><br>
> The third case is more interesting -- the cause is more subtle and more likely (IMHO) to occur in "real" code. Here's a second pass at it, with a bit of cleanup. It looks like I've created a cycle between the QWidget (whose destroyed signal refers to foo) and the function foo (which retains a reference to the QWidget). When Python breaks the cycle during its garbage collection, it finalizes the QWidget, which invokes foo, which causes a crash. The QApplication instance, however, is still valid and has not been either finalized (that is, Python's __del__) or destroyed (the C++ destructor). Any thoughts?<br>
><br>
> import sys<br>
> import os<br>
> import platform<br>
> import gc<br>
> import sip<br>
> from PyQt5.QtWidgets import QApplication, QWidget<br>
> from PyQt5.QtCore import QT_VERSION_STR<br>
> from PyQt5.Qt import PYQT_VERSION_STR<br>
><br>
> app = None<br>
><br>
> DEBUG = False<br>
><br>
> if DEBUG:<br>
> def printDestroyed(qObject):<br>
> qObjectStr = str(qObject)<br>
> def destroyed():<br>
> print('destroyed {}'.format(qObjectStr))<br>
> qObject.destroyed.connect(destroyed)<br>
><br>
> class QApplicationF(QApplication):<br>
> def __del__(self):<br>
> print('finalized {}'.format(self))<br>
><br>
> class QWidgetF(QWidget):<br>
> def __del__(self):<br>
> print('finalized {}'.format(self))<br>
><br>
> def checkLeaks():<br>
> for o in gc.get_objects():<br>
> try:<br>
> if not sip.isdeleted(o):<br>
> sip.dump(o)<br>
> except TypeError:<br>
> pass<br>
><br>
> else:<br>
> def printDestroyed(_): pass<br>
> QApplicationF = QApplication<br>
> QWidgetF = QWidget<br>
> def checkLeaks(): pass<br>
><br>
> def foo(x):<br>
> return lambda: x<br>
><br>
> def qtMain(doCrash):<br>
> global app<br>
> app = QApplicationF(sys.argv)<br>
> printDestroyed(app)<br>
><br>
> w = QWidgetF()<br>
> printDestroyed(w)<br>
><br>
> param = w if doCrash else 1<br>
> w.destroyed.connect(foo(param))<br>
><br>
> def gcCrash(createCycle, doCollect):<br>
> qtMain(createCycle)<br>
> checkLeaks()<br>
> gc.collect(doCollect)<br>
><br>
> if __name__ == '__main__':<br>
> # Both must be True for a crash.<br>
> gcCrash(True, True)<br>
<br>
</div></div>Attached is a patch (also in tonight's PyQt5 snapshot but you'll need a SIP snapshot as well) that will stop this particular crash.<br>
<br>
Note that as soon as you start using __del__ the behaviour of the garbage collector changes.<br>
<span class="HOEnZb"><font color="#888888"><br>
Phil<br>
<br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature">Bryan A. Jones, Ph.D.<br>Associate Professor<br>Department of Electrical and Computer Engineering<br>231 Simrall / PO Box 9571<br>Mississippi State University<br>Mississippi state, MS 39762<br><a href="http://www.ece.msstate.edu/~bjones" target="_blank">http://www.ece.msstate.edu/~bjones</a><br>bjones AT ece DOT msstate DOT edu<br>voice 662-325-3149<br>fax 662-325-2298<br><br>Our Master, Jesus Christ, is on his way. He'll show up right on<br>time, his arrival guaranteed by the Blessed and Undisputed Ruler,<br>High King, High God.<br>- 1 Tim. 6:14b-15 (The Message)<br></div>
</div>