Hi!
Thanks for the great library! We might have found a small issue that causes the assert
|
ntodo = len(loop._ready) |
|
|
|
task = asyncio.create_task(coro, **kwargs) |
|
# if task._source_traceback: |
|
# del task._source_traceback[-1] |
|
|
|
assert len(loop._ready) == ntodo + 1 |
|
handle = loop._ready.pop() |
to trigger when running functions in a thread using
run_in_executor. With some bad luck the thread finishes exactly when the task is created and a new handle is added causing
len(loop._ready) == ntodo + 2.
Uncommenting the monkey patch of the _ready deque shows what's happening:
import asyncio
import sys
import threading
import qtinter
from PySide6 import QtWidgets
class CrashWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setLayout(QtWidgets.QVBoxLayout())
btn = QtWidgets.QPushButton('Crash!')
btn.clicked.connect(qtinter.asyncslot(self._run_in_executor))
btn.clicked.connect(qtinter.asyncslot(self._set_event))
btn.clicked.connect(qtinter.asyncslot(self._print_hello_world))
self.layout().addWidget(btn)
def _wait_until_event(self):
self._event.wait()
print('Joining')
async def _run_in_executor(self):
self._event = threading.Event()
self._executor_task = asyncio.get_event_loop().run_in_executor(None, self._wait_until_event)
await self._executor_task
async def _set_event(self):
self._event.set()
async def _print_hello_world(self):
print('Hello World')
if __name__ == '__main__':
print(f'Main thread id: {threading.get_ident()}')
app = QtWidgets.QApplication(sys.argv)
with qtinter.using_asyncio_from_qt():
# Useful for debugging
# import collections
# class MonkeyDeque(collections.deque):
# def append(self, __x):
# print(f'Adding {__x} to deque in thread {threading.get_ident()}')
# super().append(__x)
# loop = asyncio.get_event_loop()
# loop._ready = MonkeyDeque(loop._ready)
widget = CrashWidget()
widget.show()
app.exec()
Log
Main thread id: 26280
...
Adding <Handle _chain_future.<locals>._set_state(<Future pendi...ask_wakeup()]>, <Future at 0x...rned NoneType>) at C:\dev\envs\py311\Lib\asyncio\futures.py:381> to deque in thread 17728
...
Traceback (most recent call last):
File "C:\dev\projects\the_project\venv\Lib\site-packages\qtinter\_helpers.py", line 120, in handle
transform(method, args, *extra)
File "C:\dev\projects\the_project\venv\Lib\site-packages\qtinter\_slots.py", line 29, in _run_coroutine_function
task = task_runner(coro) # TODO: set name and context
^^^^^^^^^^^^^^^^^
File "C:\dev\projects\the_project\venv\Lib\site-packages\qtinter\_tasks.py", line 30, in run_task
assert len(loop._ready) == ntodo + 1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError
The _chain_future Handle is added not from the main thread. The assert fails immediately after that.
It takes a couple of tries to trigger the assert.
Best
Tobias
Hi!
Thanks for the great library! We might have found a small issue that causes the assert
qtinter/src/qtinter/_tasks.py
Lines 24 to 31 in b30b06b
to trigger when running functions in a thread using
run_in_executor. With some bad luck the thread finishes exactly when the task is created and a new handle is added causinglen(loop._ready) == ntodo + 2.Uncommenting the monkey patch of the
_readydeque shows what's happening:Log
The
_chain_futureHandle is added not from the main thread. The assert fails immediately after that.It takes a couple of tries to trigger the assert.
Best
Tobias