Open AlexanderWells-diamond opened 11 months ago
I've run into this issue, which results asyncSlot
callbacks being cancelled unexpectedly. My solution is to patch the asyncSlot
function to use the workaround suggested by the create_task docs:
import asyncio
import functools
import inspect
import sys
from PyQt5.QtCore import pyqtSlot
background_tasks = set()
def asyncSlot(*args, **kwargs):
"""Make a Qt async slot run on asyncio loop.
Patched version of qasync.asyncSlot that keeps references to tasks,
so that they don't get garbage collected.
"""
def _error_handler(task):
try:
task.result()
except Exception:
sys.excepthook(*sys.exc_info())
finally:
background_tasks.discard(task)
def outer_decorator(fn):
@pyqtSlot(*args, **kwargs)
@functools.wraps(fn)
def wrapper(*args, **kwargs):
# Qt ignores trailing args from a signal but python does
# not so inspect the slot signature and if it's not
# callable try removing args until it is.
task = None
while len(args):
try:
inspect.signature(fn).bind(*args, **kwargs)
except TypeError:
if len(args):
# Only convert args to a list if we need to pop()
args = list(args)
args.pop()
continue
else:
task = asyncio.ensure_future(fn(*args, **kwargs))
task.add_done_callback(_error_handler)
background_tasks.add(task)
break
if task is None:
raise TypeError("asyncSlot was not callable from Signal. Potential signature mismatch.")
return task
return wrapper
return outer_decorator
If this is an acceptable way of fixing this bug, I'd be happy to open a PR.
The aiohttp_fetch.py example file makes use of the
@asyncSlot()
decorator to allow passing theon_btnFetch_clicked
async function to the.connect
method of a button.Under the covers,
@asyncSlot()
is just creating aTask
object usingasyncio.ensure_future()
. In the documentation for that function, it says "Save a reference to the result of this function, to avoid a task disappearing mid-execution.". It seems that the example code does not do this, so may be liable for garbage-collection issues if the now reference-less task is cleaned up unexpectedly.Is there a preferred pattern to use for these decorators? Or can the decorator be modified to keep a reference to the Task that is created?