[Interest] QWebEngineUrlRequestJob and asynchronous I/O

Jérôme Laheurte jerome at jeromelaheurte.net
Tue Feb 21 12:58:48 CET 2017


Hello. I already asked this on the PyQt mailing list but I guess it was not the right place; this is more of a Qt problem. 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.8/Qt 5.8). 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. Didn’t try on Windows yet.

TIA
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):
       super().__init__()
       self.open(self.ReadOnly | self.Unbuffered)
       self._data = b''
       QtCore.QTimer.singleShot(1000, self._dataReceived)

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

   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')
       super().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):
       super().__init__()
       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>')
       self.setCentralWidget(self._view)
       self.show()
       self.raise_()


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


More information about the Interest mailing list