[PyQt] Conflict between setuptools & requirements in official PyQt5 docs
kovid at kovidgoyal.net
Thu Feb 11 03:54:39 GMT 2016
Apparently, changing any python statements in that script greatly
reduces the probability of crashing, for example, simply removing the if
statement causes the crash probability to become very low on my system.
I assume this is because the order in which the garbage collector breaks
cycles is dependent on some internal interpreter state, that is
influenced by the exact code being evaluated.
On Thu, Feb 11, 2016 at 08:57:59AM +0530, Kovid Goyal wrote:
> And here's an example that crashes on exit, reliably 100% of the time
> (python3, linux)
> from PyQt5.Qt import QApplication, QMainWindow
> class MainWindow(QMainWindow):
> def closeEvent(self, ev):
> QApplication.instance().window = None
> if __name__ == '__main__':
> app = QApplication()
> app.window = MainWindow()
> Simply changing the order of the two statements in closeEvent() causes
> the crash to stop happening.
> On Wed, Feb 10, 2016 at 03:42:53PM +0000, Phil Thompson wrote:
> > On 9 Feb 2016, at 4:37 am, Kovid Goyal <kovid at kovidgoyal.net> wrote:
> > >
> > > Surely, the advice should be to keep a module level reference to the
> > > application global rather than to run code at module level. Like this:
> > >
> > > app = None
> > >
> > > def main():
> > > global app
> > > app = QApplication()
> > > app.exec_()
> > >
> > > That way you get both behaviors. Although, in my experience, you cannot
> > > avoid segfaults on exit by relying on sip.setdestroyonexit().
> > It might be worth taking a step back on this...
> > Crashes are caused by C++ dtors being invoked (via the Python garbage collector) in an order that Qt is not happy with. The order is, in effect, random. Typically this happens at the end of a "scope", the most significant of which is when the interpreter exits.
> > Probably (but it is a guess) it would be best if the QApplication instance was destructed last of all.
> > PyQt5 disables the invocation of dtors when the interpreter is exiting. Therefore if the following pattern is used...
> > if __name__ = '__main__':
> > app = QApplication()
> > gui = QWidget()
> > gui.show()
> > app.exec()
> > ...there shouldn't be a problem with crashes on exit. If anybody has an example where they think this is not the case then I'd like to know.
> > However it is not always possible to follow that pattern - setuptools requires a function entry point. When that function (ie. "scope") returns then the local objects can be garbage collected in a random order. Because the interpreter is still running, the dtors are still invoked.
> > PyQt5 tries to mitigate this to a certain extent. When a QApplication is garbage collected it first makes sure that any top-level widgets that still exist are owned by C++. This effectively disables the dtors of those widgets. However it does mean that widgets may still outlive the C++ QApplication instance - and maybe Qt doesn't like that.
> > The suggestion above (ie. the global reference to the QApplication object) has the effect of guaranteeing that the QApplication instance will outlive any objects that are garbage collected when main() returns. In fact the QApplication dtor will never be invoked as the object will only be garbage collected when the interpreter exits.
> > This pattern, therefore, should also avoid any crashes on exit. Again, if anybody has a counter example then I'd like to know.
> > I am considering changing the behaviour when the QApplication object gets garbage collected. Instead of transferring ownership of any top-level widgets it would instead explicitly invoke their dtors. This would guarantee the QApplication outlives any widgets (as the global reference trick does) but it would not be able to make the same guarantee regarding any other objects that might be referenced at the global level. However I think it would be an improvement over the current behaviour.
> > PyQt4 has a mechanism for tracking objects of certain classes (actually it is only done for QSystemTrayIcon) so that they get handled in a similar way to top-level widgets. I'd consider adding the same mechanism to PyQt5 if it turned out that crashes always involved certain classes. I'd also consider exposing that mechanism to applications to that they could add specific objects to a cleanup handler that is invoked just before the QApplication dtor.
> > As ever, feedback and comments welcome.
> > Phil
> > _______________________________________________
> > PyQt mailing list PyQt at riverbankcomputing.com
> > https://www.riverbankcomputing.com/mailman/listinfo/pyqt
> Dr. Kovid Goyal
> PyQt mailing list PyQt at riverbankcomputing.com
Dr. Kovid Goyal
More information about the PyQt