Don't pass Python Future objects to QML

Returning a Future doesn't work on Windows for some reason
(https://github.com/thp/pyotherside/issues/116).

Instead of using these objects from QML to cancel running coroutines,
call a Python QMLBridge function that takes a coroutine UUID and will
take care of the cancelling.
This commit is contained in:
miruka
2020-09-28 23:06:50 -04:00
parent 53d2ab17af
commit ee1091b4dc
20 changed files with 161 additions and 178 deletions

View File

@@ -21,7 +21,7 @@ import traceback
from concurrent.futures import Future
from operator import attrgetter
from threading import Thread
from typing import Coroutine, Sequence
from typing import Coroutine, Dict, Sequence
import pyotherside
@@ -52,6 +52,8 @@ class QMLBridge:
from .backend import Backend
self.backend: Backend = Backend()
self._running_futures: Dict[str, Future] = {}
Thread(target=self._start_asyncio_loop).start()
@@ -73,7 +75,7 @@ class QMLBridge:
self._loop.run_forever()
def _call_coro(self, coro: Coroutine, uuid: str) -> Future:
def _call_coro(self, coro: Coroutine, uuid: str) -> None:
"""Schedule a coroutine to run in our thread and return a `Future`."""
def on_done(future: Future) -> None:
@@ -87,27 +89,37 @@ class QMLBridge:
trace = traceback.format_exc().rstrip()
CoroutineDone(uuid, result, exception, trace)
del self._running_futures[uuid]
future = asyncio.run_coroutine_threadsafe(coro, self._loop)
self._running_futures[uuid] = future
future.add_done_callback(on_done)
return future
def call_backend_coro(
self, name: str, uuid: str, args: Sequence[str] = (),
) -> Future:
"""Schedule a `Backend` coroutine and return a `Future`."""
) -> None:
"""Schedule a coroutine from the `QMLBridge.backend` object."""
return self._call_coro(attrgetter(name)(self.backend)(*args), uuid)
self._call_coro(attrgetter(name)(self.backend)(*args), uuid)
def call_client_coro(
self, user_id: str, name: str, uuid: str, args: Sequence[str] = (),
) -> Future:
"""Schedule a `MatrixClient` coroutine and return a `Future`."""
) -> None:
"""Schedule a coroutine from a `QMLBridge.backend.clients` client."""
client = self.backend.clients[user_id]
return self._call_coro(attrgetter(name)(client)(*args), uuid)
self._call_coro(attrgetter(name)(client)(*args), uuid)
def cancel_coro(self, uuid: str) -> None:
"""Cancel a couroutine scheduled by the `QMLBridge` methods."""
try:
self._running_futures[uuid].cancel()
except KeyError:
log.warning("Couldn't cancel coroutine %s, future not found", uuid)
def pdb(self, additional_data: Sequence = ()) -> None: