Description
The following exception is thrown occasionally in qwebchannel.py:
Task exception was never retrieved
future: <Task finished coro=<QWebChannelWebSocketProtocol.read_msgs() done, defined at scancontrolclient.py:36> exception=KeyError('scancontrol',)>
Traceback (most recent call last):
File "scancontrolclient.py", line 38, in read_msgs
self.webchannel.message_received(msg)
File "qwebchannel.py", line 64, in message_received
self.handleSignal(data);
File "qwebchannel.py", line 98, in handleSignal
object = self.objects[message["object"]];
KeyError: 'scancontrol'
It happens when a signal is received for an object that not yet exists in the list of qobjects in the webchannel instance.
This might happen e.g. if the host directly sends signals after connecting. The target object was not yet created, yet qwebchannel already tries to handle it.
In my actual case, I try to establish a connection. As soon as it's done, I await qwebchannel for being complete with creating the objects. However, even before this is done, the exception is thrown because a signal is received and processed in the event loop, whithout the object being created.
The remote software sends a lot of data thorugh a signal when a measurement is running.
After connecting and awaiting webchannel to be finished, such a signal is received and crashes with the exception above.
Here are the files I used:
issue_example.zip
For reproducing the error, simply open ScanControl dummy version and press start. After this, run example1.py.
Maybe there is a way of preventing the signals to be processed before object creation has finished?
Here's my protocol implementation. Maybe I can do message receiving in a different way without changes in qwebchannel.py:
class QWebChannelWebSocketProtocol(websockets.client.WebSocketClientProtocol):
""" Bridges WebSocketClientProtocol and QWebChannel.
Continuously reads messages in a task and invokes QWebChannel.message_received()
for each. Calls QWebChannel.connection_open() when connected.
Also patches QWebChannel.send() to run the websocket's send() in a task"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def _task_send(self, data):
if not isinstance(data, str):
data = json.dumps(data)
self.loop.create_task(self.send(data))
def connection_open(self):
super().connection_open()
self.webchannel = QWebChannel()
self.webchannel.send = self._task_send
self.webchannel.connection_made(self)
self.loop.create_task(self.read_msgs())
async def read_msgs(self):
async for msg in self:
self.webchannel.message_received(msg)