<div dir="ltr"><div>I am writing a web radio player as personal project. Other than accessing the media stream, it also access some related files (json for the program list, related images, etc.)</div><div><br></div><div>Since I'm in the middle of the development, I've not dotted all i's yet. Specifically, some QNetworkReply objects don't get deleted yet once I'm done with them (which I'll eventually do with deleteLater as expected, but I'll need to find a proper way to handle that while providing a common interface).</div><div><br></div><div>Some of those replies work by simply referencing the reply itself in a local function.</div><div>For example, this is a simplified version of some code that gets a remote image:<br></div><div><br></div><div><span style="font-family:monospace">def getRemoteImage(self, someObject):</span><br></div><div><span style="font-family:monospace">    # "self.nam" is the common QNetworkAccessManager<br></span></div><div><span style="font-family:monospace">    reply = self.nam.get(QNetworkRequest(someObject.url()))</span></div><div><span style="font-family:monospace">    def doSomeStuff():<br></span></div><span style="font-family:monospace">        pm = QPixmap()</span><div><span style="font-family:monospace">        if pm.loadFromData(reply.readAll()):<br></span></div><div><span style="font-family:monospace">            someObject.setIcon(QIcon(pm))</span></div><div><span style="font-family:monospace">    reply.finished.connect(doSomeStuff)</span></div><div><br></div><div>I know that this is not very elegant, but it's concise, and it works for simple situations, without requiring a more comprehensive implementation that isn't based on local context.</div><div><br></div><div>Well, at least, I <i>thought</i> it worked.</div><div>After some time and different testing, I stumbled upon unexpected segfaults. The result of my research is that if the replies are not deleted, at some point the program crashes.</div><div>What I found interesting is that what *seems* to trigger the crash is the number of connections done to the reply, like there's a total amount of possible existing replies and their scoped references.</div><div><br></div><div>I prepared a MRE that should be able to show the problem:</div><div><br></div><div><span style="font-family:monospace">from PyQt5.QtCore import *<br>from PyQt5.QtWidgets import *<br>from PyQt5.QtNetwork import *<br><br>class Test(QWidget):<br>    def __init__(self):<br>        super().__init__()<br>        btn = QPushButton('Test')<br>        QVBoxLayout(self).addWidget(btn)<br><br>        self.nam = QNetworkAccessManager()<br>        btn.clicked.connect(btn.setEnabled)<br>        btn.clicked.connect(self.download)<br><br>        self.timer = QTimer(self, timeout=self.download, interval=100)<br>        self.count = 0<br><br>        base = '<a href="https://www.google.com/search?q={}">https://www.google.com/search?q={}</a>'<br>        self.urls = [QUrl(base.format(i)) for i in range(100)]<br><br>    def download(self):<br>        if not self.timer.isActive():<br>            self.timer.start()<br>        url = self.urls.pop(0)<br>        reply = self.nam.get(QNetworkRequest(url))<br><br>        def test1():<br>            self.count += 1<br>            print(self.count, 'finished (first connect)')<br>        def test2():<br>            pass<br>        def test3():<br>            pass<br>        def testWithReference():<br>            print(self.count, 'finished > reference to reply:', reply)<br>            print()<br><br>        reply.finished.connect(test1)<br>        reply.finished.connect(test2)<br>        reply.finished.connect(test3)<br><br>        reply.finished.connect(self.doSomething)<br>        reply.finished.connect(testWithReference)<br><br>        if not self.urls:<br>            self.timer.stop()<br><br>    def doSomething(self):<br>        print(self.sender())<br><br><br>app = QApplication([])<br>test = Test()<br>test.show()<br>app.exec()</span></div><div><br></div><div>I'm currently able to reproduce this on Linux with PyQt 5.15.4 (Qt 5.15.2) and PyQt/Qt 6.6.1, with Python 3.9.5.<br></div><div>Interestingly enough, this doesn't happen with an old PyQt/Qt 5.7.1 and Python 3.4.5.</div><div><br></div><div>Note that I'm completely aware that avoiding replies deletion and using local references are not recommended approaches, yet, at least based on the generic behavior of wrapped Qt objects and python scope management, it should work in theory: a similar concept could normally be easily and reliably applied with other QObjects, leading me to think that, probably, at some point there is some issue in the management of references to objects directly generated by Qt.</div><div><br></div><div>I will obviously implement a more thorough deletion mechanism for the replies, which, based on my tests, should avoid the problem completely. Yet, I wanted to report this anyway, at least in order to understand its origins (I *suppose* it's a Python problem, but I'm not 100% sure), what causes it and know more about the underlying aspects.</div><div>Specifically, I'd like to understand what could happen in a stress case, with dozens of current requests (and replies) that have not been completed yet.<br></div><div><br></div><div>Regards,</div><div>MaurizioB<br></div><div><br></div><div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature">È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi<br><a href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div></div></div>