Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
qtws26_logo

The Qt Event of the Year: Qt World Summit 2026

Save Your Spot >
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. QHttpMultiPart generates SIGSEGV when used multiple times
Qt 6.12 LTS is out! See what's new in the release blog

QHttpMultiPart generates SIGSEGV when used multiple times

Scheduled Pinned Locked Moved Unsolved Qt for Python
3 Posts 1 Posters 669 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    borisruna
    wrote on last edited by
    #1

    Hello Everyone!
    Using the following code, I am trying to upload images to a FastAPI http server:

    from pathlib import Path
    import sys
    from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QLineEdit, QPushButton, QTextEdit, QFileDialog, \
        QApplication
    from PyQt5.QtCore import pyqtSignal, QObject, QUrl
    from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QHttpMultiPart, QHttpPart
    
    
    class Client(QObject):
        replyReceived = pyqtSignal(str)
    
        def __init__(self):
            super().__init__()
            self.nam = QNetworkAccessManager()
            self.nam.finished.connect(self.on_reply)
    
        def make_request(self, url, data, filename):
            print(f"Uploading file to {url}")
            self.request = QNetworkRequest(url)
            self.multipart = QHttpMultiPart(QHttpMultiPart.FormDataType)
            for key, value in (data or {}).items():
                http_part = QHttpPart()
                http_part.setHeader(
                    QNetworkRequest.ContentDispositionHeader,
                    f'form-data; name="{key}"'
                )
                http_part.setBody(value.encode('utf-8'))
                self.multipart.append(http_part)
    
            if filename:
                file_part = QHttpPart()
                filedata = open(filename, 'rb').read()
                file_part.setHeader(
                    QNetworkRequest.ContentDispositionHeader,
                    f'form-data; name="uploaded_file"; filename="{Path(filename).name}"'
                )
                file_part.setBody(filedata)
                self.multipart.append(file_part)
    
            self.nam.post(self.request, self.multipart)
    
        def on_reply(self, reply):
            reply_bytes = reply.readAll()
            reply_string = bytes(reply_bytes).decode('utf-8')
            self.replyReceived.emit(reply_string)
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            widget = QWidget(minimumWidth=600)
            self.setCentralWidget(widget)
            widget.setLayout(QVBoxLayout())
            self.url = QLineEdit('http://127.0.0.1:8000/photos')
            self.fname = QPushButton('(Choose File...)', clicked=self.on_file_btn)
            submit = QPushButton('Send File', clicked=self.submit)
            response = QTextEdit(readOnly=True)
            for w in (self.url, self.fname, submit, response):
                widget.layout().addWidget(w)
            self.poster = Client()
            self.poster.replyReceived.connect(response.setText)
            self.show()
    
        def on_file_btn(self):
            filename, accepted = QFileDialog.getOpenFileName()
            if accepted:
                self.fname.setText(filename)
    
        def submit(self):
            url = QUrl(self.url.text())
            for i in range(0, 1):
                filename = '205520_20220911191143.png'
                if filename == '(No File)':
                    filename = None
                data = {'var1': 'spam'}
                self.poster.make_request(url, data, filename)
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        main_window = MainWindow()
        sys.exit(app.exec())
    

    If used once, this works as intended. However if I increase the steps in line 71 to range(0, 10) for examle, I get:
    Process finished with exit code 139 (interrupted by signal 11: SIGSEGV).
    I believe that there is something blocking and objects are being deleted in the background, however I am unable to solve the problem here.
    I would be more than grateful if someone could give more insight to my issue!
    Thank you in advance!
    Boris

    1 Reply Last reply
    0
    • B Offline
      B Offline
      borisruna
      wrote on last edited by borisruna
      #2

      @borisruna said in QHttpMultiPart generates SIGSEGV when used multiple times:

      QHttpMultiPart

      With the great help of "The Compiler / Florian Bruhin" from #qt-pyside on libera.chat, using gdb -ex r --args python3 app.py , we got the following result:

      #0  0x00007ffff59b5807 in QMetaObject::cast(QObject const*) const () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
      No symbol table info available.
      #1  0x00007ffff58e4dec in QNonContiguousByteDeviceFactory::createShared(QIODevice*) () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
      No symbol table info available.
      #2  0x00007ffff21eb1cd in QNetworkReplyHttpImplPrivate::createUploadByteDevice() () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Network.so.5
      No symbol table info available.
      #3  0x00007ffff21f18ef in QNetworkReplyHttpImplPrivate::postRequest(QNetworkRequest const&) () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Network.so.5
      No symbol table info available.
      #4  0x00007ffff21f25c2 in QNetworkReplyHttpImplPrivate::start(QNetworkRequest const&) () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Network.so.5
      --Type <RET> for more, q to quit, c to continue without paging--
      No symbol table info available.
      #5  0x00007ffff21f28db in QNetworkReplyHttpImplPrivate::_q_startOperation() () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Network.so.5
      No symbol table info available.
      #6  0x00007ffff228ffb9 in QNetworkReplyHttpImpl::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Network.so.5
      No symbol table info available.
      #7  0x00007ffff59dada3 in QObject::event(QEvent*) () from /home/b/test/venv/lib/python3.8/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
      No symbol table info available.
      

      and concluded that objects are garbage collected by Python during the upload, while Qt still needs them.
      One suggestion was to store the requests and multipart objects in a temporary list, however this would need then a proper cleanup to avoid a memory leak.
      Still working on this, will reply when finished. However if someone has other suggestions I would be more than happy to discuss them.
      Once again, many thanks to "The - Compiler" !

      1 Reply Last reply
      1
      • B Offline
        B Offline
        borisruna
        wrote on last edited by
        #3

        I also found this https://github.com/mbruel/qHttpPostFile/blob/master/HttpPoster.cpp#L54, which in my case translates to :

        from pathlib import Path
        import sys
        from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QLineEdit, QPushButton, QTextEdit, QFileDialog, \
            QApplication
        from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QEventLoop, QFile
        from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QHttpMultiPart, QHttpPart
        
        
        class Client(QObject):
            replyReceived = pyqtSignal(str)
        
            def __init__(self):
                super().__init__()
                self.nam = QNetworkAccessManager()
                self.nam.finished.connect(self.on_reply)
        
            def _create_multipart(self, url, data, filename):
                multipart = QHttpMultiPart(QHttpMultiPart.FormDataType)
                for key, value in (data or {}).items():
                    http_part = QHttpPart()
                    http_part.setHeader(
                        QNetworkRequest.ContentDispositionHeader,
                        f'form-data; name="{key}"'
                    )
                    http_part.setBody(value.encode('utf-8'))
                    multipart.append(http_part)
        
                if filename:
                    file_part = QHttpPart()
                    filedata = open(filename, 'rb').read()
                    file_part.setHeader(
                        QNetworkRequest.ContentDispositionHeader,
                        f'form-data; name="uploaded_file"; filename="{Path(filename).name}"'
                    )
                    file_part.setBody(filedata)
                    multipart.append(file_part)
                return multipart
                # self.nam.post(request, multipart)
                # self.requests.append(request)
                # self.multiparts.append(multipart)
        
            def upload(self, url, data, filename):
                url = QUrl(url)
                multipart = self._create_multipart(url=url, data=data, filename=filename)
                request = QNetworkRequest(url)
                reply = self.nam.post(request, multipart)
                multipart.setParent(reply)
                print(f'Uploading file to {url}')
        
        
            def on_reply(self, reply):
                reply_bytes = reply.readAll()
                reply_string = bytes(reply_bytes).decode('utf-8')
                print(reply_string)
                self.replyReceived.emit(reply_string)
        
        
        class MainWindow(QMainWindow):
            def __init__(self):
                super().__init__()
                widget = QWidget(minimumWidth=600)
                self.setCentralWidget(widget)
                widget.setLayout(QVBoxLayout())
                self.url = QLineEdit('http://127.0.0.1:8000/photos')
                self.fname = QPushButton('(Choose File...)', clicked=self.on_file_btn)
                submit = QPushButton('Send File', clicked=self.submit)
                response = QTextEdit(readOnly=True)
                for w in (self.url, self.fname, submit, response):
                    widget.layout().addWidget(w)
                self.poster = Client()
                self.poster.replyReceived.connect(response.setText)
                self.show()
        
            def on_file_btn(self):
                filename, accepted = QFileDialog.getOpenFileName()
                if accepted:
                    self.fname.setText(filename)
        
            def submit(self):
                url = QUrl(self.url.text())
                for i in range(0, 10):
                    filename = '205520_20220911191143.png'
                    if filename == '(No File)':
                        filename = None
                    data = {'var1': 'spam'}
                    self.poster.upload(url, data, filename)
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
            main_window = MainWindow()
            sys.exit(app.exec())
        

        This method seems to work well (at least for my tests so far)

        1 Reply Last reply
        0

        • Login

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • Users
        • Groups
        • Search
        • Get Qt
        • Unsolved