quick_frontend: Q&D adapter to use legacy callback/errback with modern bridges
This commit is contained in:
parent
e8b3948ad0
commit
d3dc59b141
3 changed files with 134 additions and 4 deletions
|
|
@ -349,7 +349,12 @@ class QuickApp:
|
||||||
log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
|
log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
|
||||||
|
|
||||||
def on_bridge_connected(self):
|
def on_bridge_connected(self):
|
||||||
self.bridge.ready_get(self.on_backend_ready)
|
d = self.bridge.ready_get()
|
||||||
|
d.addErrback(self._ready_get_eb)
|
||||||
|
d.addCallback(lambda __: self.on_backend_ready())
|
||||||
|
|
||||||
|
def _ready_get_eb(self, failure_):
|
||||||
|
log.error("Can't check backend readiness: {msg}".format(msg=failure_))
|
||||||
|
|
||||||
def _bridge_cb(self):
|
def _bridge_cb(self):
|
||||||
self.register_signal("connected")
|
self.register_signal("connected")
|
||||||
|
|
|
||||||
118
libervia/frontends/quick_frontend/quick_bridge.py
Normal file
118
libervia/frontends/quick_frontend/quick_bridge.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Libervia: an XMPP client
|
||||||
|
# Copyright (C) 2009-2026 Jérôme Poisson (goffi@goffi.org)
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Adapte the legacy callback/errback bridge API to bridges"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import inspect
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
from twisted.internet import defer
|
||||||
|
from twisted.python.failure import Failure
|
||||||
|
|
||||||
|
from libervia.backend.core.log import getLogger
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class QuickBridgeAdapter:
|
||||||
|
"""Adapt legacy callback/errback to bridges
|
||||||
|
|
||||||
|
@param bridge: bridge instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, bridge: Any) -> None:
|
||||||
|
self._bridge = bridge
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> Any:
|
||||||
|
attr = getattr(self._bridge, name)
|
||||||
|
if not callable(attr):
|
||||||
|
return attr
|
||||||
|
|
||||||
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||||
|
callback = kwargs.pop("callback", None)
|
||||||
|
errback = kwargs.pop("errback", None)
|
||||||
|
result = attr(*args, **kwargs)
|
||||||
|
if inspect.iscoroutine(result):
|
||||||
|
result = asyncio.ensure_future(result)
|
||||||
|
if isinstance(result, asyncio.Future):
|
||||||
|
# async bridges (e.g. AIOBridge) return asyncio futures
|
||||||
|
result = defer.Deferred.fromFuture(result)
|
||||||
|
if (callback is None and errback is None) or not hasattr(
|
||||||
|
result, "addCallback"
|
||||||
|
):
|
||||||
|
return result
|
||||||
|
if callback is not None:
|
||||||
|
result.addCallback(callback)
|
||||||
|
if errback is not None:
|
||||||
|
result.addErrback(errback)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def register_signal(self, name: str, handler: Callable, iface: str = "core") -> None:
|
||||||
|
"""Register a signal handler, adapting sync handlers to async bridges
|
||||||
|
|
||||||
|
@param name: signal name
|
||||||
|
@param handler: signal handler, sync or async
|
||||||
|
@param iface: bridge interface ("core" or "plugin")
|
||||||
|
"""
|
||||||
|
if not inspect.iscoroutinefunction(handler):
|
||||||
|
|
||||||
|
async def async_handler(*args: Any,
|
||||||
|
_handler: Callable = handler) -> None:
|
||||||
|
_handler(*args)
|
||||||
|
|
||||||
|
handler = async_handler
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._bridge.register_signal(name, handler, iface)
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Signal {name} is not available on the bridge")
|
||||||
|
|
||||||
|
def bridge_connect(self, callback: Callable | None = None,
|
||||||
|
errback: Callable | None = None) -> None:
|
||||||
|
"""Connect to the bridge
|
||||||
|
|
||||||
|
@param callback: called when the bridge is connected
|
||||||
|
@param errback: called with a twisted Failure on connection error
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _connect() -> None:
|
||||||
|
try:
|
||||||
|
await self._bridge.bridge_connect()
|
||||||
|
except Exception as e:
|
||||||
|
if errback is not None:
|
||||||
|
errback(Failure(e))
|
||||||
|
else:
|
||||||
|
if callback is not None:
|
||||||
|
callback()
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
# the event loop is not running yet (frontends usually call connect_bridge()
|
||||||
|
# before starting their loop)
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.call_soon(asyncio.ensure_future, _connect())
|
||||||
|
else:
|
||||||
|
asyncio.ensure_future(_connect())
|
||||||
|
|
@ -233,9 +233,16 @@ class QuickProfileManager(object):
|
||||||
|
|
||||||
def refill_profiles(self):
|
def refill_profiles(self):
|
||||||
"""Rebuild the list of profiles"""
|
"""Rebuild the list of profiles"""
|
||||||
profiles = self.host.bridge.profiles_list_get()
|
self.host.bridge.profiles_list_get(
|
||||||
profiles.sort()
|
callback=self._profiles_loaded,
|
||||||
self.set_profiles(profiles)
|
errback=self._profiles_list_eb,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _profiles_loaded(self, profiles):
|
||||||
|
self.set_profiles(sorted(profiles))
|
||||||
|
|
||||||
|
def _profiles_list_eb(self, failure_):
|
||||||
|
log.error("Can't get profiles list: {msg}".format(msg=failure_))
|
||||||
|
|
||||||
## Method which must be implemented by frontends ##
|
## Method which must be implemented by frontends ##
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue