[PyQt] Behavior of QWebEngineUrlRequestJob

Jérôme Laheurte jerome at jeromelaheurte.net
Tue Feb 7 09:15:21 GMT 2017

Hello. I’m trying to implement a custom URI scheme handler, in a context where the underlying data will actually be fetched asynchronously. I reduced the problem to the « minimal » code pasted below (python 3.5, PyQt 5.7.1). Basically the problem is that when I call QWebEngineUrlRequestJob.reply() with an instance of my custom QIODevice, I expect the following:

1. bytesAvailable() is called, returns 0 (no data available yet)
2. The request job waits for readyRead to be emitted
3. The proceeds with bytesAvailable() / readData() until data is exhausted
4. Goto 2, until readChannelFinished is emitted

What actually happens:

1. bytesAvailable() is called, returns 0
2. The request job tries to read 32 Kb nonetheless
3. It gets no data, closes the QIODevice

Did I miss something here ? This happens on both mac OS and Linux.

Jérôme Laheurte

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler

class TestIODevice(QtCore.QIODevice):
    def __init__(self):
        self.open(self.ReadOnly | self.Unbuffered)
        self._data = b''
        QtCore.QTimer.singleShot(1000, self._dataReceived)

    def _dataReceived(self):
        print('== RECV')
        self._data = b'foo'

    def bytesAvailable(self):
        count = len(self._data) + super().bytesAvailable()
        print('== AVAIL', count)
        return count

    def isSequential(self):
        return True

    def readData(self, maxSize):
        print('== READ', maxSize)
        data, self._data = self._data[:maxSize], self._data[maxSize:]
        return data

    def close(self):
        print('== CLOSE')

class TestHandler(QWebEngineUrlSchemeHandler):
    def requestStarted(self, request):
        self._dev = TestIODevice() # Must keep ref
        request.reply(b'text/plain', self._dev)

class TestWindow(QtWidgets.QMainWindow):
    def __init__(self):
        self._view = QWebEngineView(self)
        self._handler = TestHandler() # Must keep ref
        self._view.page().profile().installUrlSchemeHandler(b'spam', self._handler)
        self._view.setHtml('<html><head><title>Test</title></head><body><img src="spam:eggs" /></body></html>')

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    win = TestWindow()

More information about the PyQt mailing list