changeset 4167:319a0e47dc8b

plugin ad-hoc D-Bus: fix deprecated use of python-dbus: the plugin was using python-dbus which is deprecated in the backend in the favor of TxDBus. Methods calls have been updated, and the plugin works again, but there seems to be still some issues (warnings in the logs). Those will be fixed later.
author Goffi <goffi@goffi.org>
date Fri, 01 Dec 2023 15:22:55 +0100
parents a1f7040b5a15
children d67eaa684484
files libervia/backend/plugins/plugin_adhoc_dbus.py libervia/cli/cmd_adhoc.py
diffstat 2 files changed, 78 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_adhoc_dbus.py	Thu Nov 30 13:23:53 2023 +0100
+++ b/libervia/backend/plugins/plugin_adhoc_dbus.py	Fri Dec 01 15:22:55 2023 +0100
@@ -17,14 +17,21 @@
 # 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/>.
 
+from collections import OrderedDict
+import os.path
+import uuid
+
+from twisted.internet import defer
+from twisted.internet import reactor
+from twisted.words.protocols.jabber import jid
+from wokkel import data_form
+
+from libervia.backend.core.constants import Const as C
 from libervia.backend.core.i18n import D_, _
-from libervia.backend.core.constants import Const as C
 from libervia.backend.core.log import getLogger
 
 log = getLogger(__name__)
-from twisted.internet import defer
-from twisted.words.protocols.jabber import jid
-from wokkel import data_form
+
 
 try:
     from lxml import etree
@@ -32,19 +39,17 @@
     etree = None
     log.warning("Missing module lxml, please download/install it from http://lxml.de/ ."
                 "Auto D-Bus discovery will be disabled")
-from collections import OrderedDict
-import os.path
-import uuid
+
 try:
-    import dbus
-    from dbus.mainloop.glib import DBusGMainLoop
+    import txdbus
+    from txdbus import client as dbus_client
 except ImportError:
-    dbus = None
-    log.warning("Missing module dbus, please download/install it, "
-                "auto D-Bus discovery will be disabled")
+    txdbus = None
+    log.warning(
+        "Missing module txdbus, please download/install it, auto D-Bus discovery will be "
+        "disabled"
+    )
 
-else:
-    DBusGMainLoop(set_as_default=True)
 
 NS_MEDIA_PLAYER = "org.libervia.mediaplayer"
 FD_NAME = "org.freedesktop.DBus"
@@ -92,10 +97,10 @@
 }
 
 
-class AdHocDBus(object):
+class AdHocDBus:
 
     def __init__(self, host):
-        log.info(_("plugin Ad-Hoc D-Bus initialization"))
+        log.info(f"plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization")
         self.host = host
         if etree is not None:
             host.bridge.add_method(
@@ -116,13 +121,14 @@
         )
         self._c = host.plugins["XEP-0050"]
         host.register_namespace("mediaplayer", NS_MEDIA_PLAYER)
-        if dbus is not None:
-            self.session_bus = dbus.SessionBus()
-            self.fd_object = self.session_bus.get_object(
-                FD_NAME, FD_PATH, introspect=False)
+        self.session_con = None
 
-    def profile_connected(self, client):
-        if dbus is not None:
+    async def profile_connected(self, client):
+        if txdbus is not None:
+            if self.session_con is None:
+                self.session_con = await dbus_client.connect(reactor, 'session')
+                self.fd_object = await self.session_con.getRemoteObject(FD_NAME, FD_PATH)
+
             self._c.add_ad_hoc_command(
                 client, self.local_media_cb, D_("Media Players"),
                 node=NS_MEDIA_PLAYER,
@@ -130,7 +136,7 @@
                                  # in the middle of a movie
             )
 
-    def _dbus_async_call(self, proxy, method, *args, **kwargs):
+    async def _dbus_async_call(self, proxy, method, *args, **kwargs):
         """ Call a DBus method asynchronously and return a deferred
 
         @param proxy: DBus object proxy, as returner by get_object
@@ -142,23 +148,18 @@
         @return: a deferred
 
         """
-        d = defer.Deferred()
-        interface = kwargs.pop("interface", None)
-        kwargs["reply_handler"] = lambda ret=None: d.callback(ret)
-        kwargs["error_handler"] = d.errback
-        proxy.get_dbus_method(method, dbus_interface=interface)(*args, **kwargs)
-        return d
+        return await proxy.callRemote(method, *args, **kwargs)
 
-    def _dbus_get_property(self, proxy, interface, name):
-        return self._dbus_async_call(
+    async def _dbus_get_property(self, proxy, interface, name):
+        return await self._dbus_async_call(
             proxy, "Get", interface, name, interface="org.freedesktop.DBus.Properties")
 
 
-    def _dbus_list_names(self):
-        return self._dbus_async_call(self.fd_object, "ListNames")
+    async def _dbus_list_names(self):
+        return await self.fd_object.callRemote("ListNames")
 
-    def _dbus_introspect(self, proxy):
-        return self._dbus_async_call(proxy, INTROSPECT_METHOD, interface=INTROSPECT_IFACE)
+    async def _dbus_introspect(self, proxy):
+        return await self._dbus_async_call(proxy, INTROSPECT_METHOD, interface=INTROSPECT_IFACE)
 
     def _accept_method(self, method):
         """ Return True if we accept the method for a command
@@ -172,18 +173,18 @@
             return False
         return True
 
-    @defer.inlineCallbacks
-    def _introspect(self, methods, bus_name, proxy):
+    async def _introspect(self, methods, bus_name, proxy):
         log.debug("introspecting path [%s]" % proxy.object_path)
-        introspect_xml = yield self._dbus_introspect(proxy)
+        assert etree is not None
+        introspect_xml = await self._dbus_introspect(proxy)
         el = etree.fromstring(introspect_xml)
         for node in el.iterchildren("node", "interface"):
             if node.tag == "node":
                 new_path = os.path.join(proxy.object_path, node.get("name"))
-                new_proxy = self.session_bus.get_object(
-                    bus_name, new_path, introspect=False
+                new_proxy = await self.session_con.getRemoteObject(
+                    bus_name, new_path
                 )
-                yield self._introspect(methods, bus_name, new_proxy)
+                await self._introspect(methods, bus_name, new_proxy)
             elif node.tag == "interface":
                 name = node.get("name")
                 if any(name.startswith(ignored) for ignored in IGNORED_IFACES_START):
@@ -203,24 +204,26 @@
             client, prog_name, allowed_jids, allowed_groups, allowed_magics,
             forbidden_jids, forbidden_groups, flags)
 
-    @defer.inlineCallbacks
-    def ad_hoc_dbus_add_auto(self, client, prog_name, allowed_jids=None, allowed_groups=None,
+    async def ad_hoc_dbus_add_auto(self, client, prog_name, allowed_jids=None, allowed_groups=None,
                          allowed_magics=None, forbidden_jids=None, forbidden_groups=None,
                          flags=None):
-        bus_names = yield self._dbus_list_names()
+        bus_names = await self._dbus_list_names()
         bus_names = [bus_name for bus_name in bus_names if "." + prog_name in bus_name]
         if not bus_names:
             log.info("Can't find any bus for [%s]" % prog_name)
-            defer.returnValue(("", []))
+            return ("", [])
         bus_names.sort()
         for bus_name in bus_names:
             if bus_name.endswith(prog_name):
                 break
-        log.info("bus name found: [%s]" % bus_name)
-        proxy = self.session_bus.get_object(bus_name, "/", introspect=False)
+        else:
+            log.info(f"Can't find any command for {prog_name}")
+            return ("", [])
+        log.info(f"bus name found: {bus_name}")
+        proxy = await self.session_con.getRemoteObject(bus_name, "/")
         methods = set()
 
-        yield self._introspect(methods, bus_name, proxy)
+        await self._introspect(methods, bus_name, proxy)
 
         if methods:
             self._add_command(
@@ -236,7 +239,7 @@
                 flags=flags,
             )
 
-        defer.returnValue((str(bus_name), methods))
+        return (str(bus_name), methods)
 
     def _add_command(self, client, adhoc_name, bus_name, methods, allowed_jids=None,
                     allowed_groups=None, allowed_magics=None, forbidden_jids=None,
@@ -244,7 +247,7 @@
         if flags is None:
             flags = set()
 
-        def d_bus_callback(client, command_elt, session_data, action, node):
+        async def d_bus_callback(client, command_elt, session_data, action, node):
             actions = session_data.setdefault("actions", [])
             names_map = session_data.setdefault("names_map", {})
             actions.append(action)
@@ -281,9 +284,9 @@
                     raise self._c.AdHocError(self._c.ERROR.BAD_PAYLOAD)
 
                 path, iface, command = names_map[command]
-                proxy = self.session_bus.get_object(bus_name, path)
+                proxy = await self.session_con.getRemoteObject(bus_name, path)
 
-                self._dbus_async_call(proxy, command, interface=iface)
+                await self._dbus_async_call(proxy, command, interface=iface)
 
                 # job done, we can end the session, except if we have FLAG_LOOP
                 if FLAG_LOOP in flags:
@@ -292,7 +295,7 @@
                     # is OK)
                     del actions[:]
                     names_map.clear()
-                    return d_bus_callback(
+                    return await d_bus_callback(
                         client, None, session_data, self._c.ACTION.EXECUTE, node
                     )
                 form = data_form.Form("form", title=_("Updated"))
@@ -321,15 +324,14 @@
     def _ad_hoc_remotes_get(self, profile):
         return self.ad_hoc_remotes_get(self.host.get_client(profile))
 
-    @defer.inlineCallbacks
-    def ad_hoc_remotes_get(self, client):
+    async def ad_hoc_remotes_get(self, client):
         """Retrieve available remote media controlers in our devices
         @return (list[tuple[unicode, unicode, unicode]]): list of devices with:
             - entity full jid
             - device name
             - device label
         """
-        found_data = yield defer.ensureDeferred(self.host.find_by_features(
+        found_data = await defer.ensureDeferred(self.host.find_by_features(
             client, [self.host.ns_map['commands']], service=False, roster=False,
             own_jid=True, local_device=True))
 
@@ -338,11 +340,11 @@
         for found in found_data:
             for device_jid_s in found:
                 device_jid = jid.JID(device_jid_s)
-                cmd_list = yield self._c.list(client, device_jid)
+                cmd_list = await self._c.list(client, device_jid)
                 for cmd in cmd_list:
                     if cmd.nodeIdentifier == NS_MEDIA_PLAYER:
                         try:
-                            result_elt = yield self._c.do(client, device_jid,
+                            result_elt = await self._c.do(client, device_jid,
                                                           NS_MEDIA_PLAYER, timeout=5)
                             command_elt = self._c.get_command_elt(result_elt)
                             form = data_form.findForm(command_elt, NS_MEDIA_PLAYER)
@@ -366,9 +368,9 @@
                                 "Can't retrieve remote controllers on {device_jid}: "
                                 "{reason}".format(device_jid=device_jid, reason=e)))
                         break
-        defer.returnValue(remotes)
+        return remotes
 
-    def do_mpris_command(self, proxy, command):
+    async def do_mpris_command(self, proxy, command):
         iface, command = command.rsplit(".", 1)
         if command == CMD_GO_BACK:
             command = 'Seek'
@@ -378,7 +380,7 @@
             args = [SEEK_OFFSET]
         else:
             args = []
-        return self._dbus_async_call(proxy, command, *args, interface=iface)
+        return await self._dbus_async_call(proxy, command, *args, interface=iface)
 
     def add_mpris_metadata(self, form, metadata):
         """Serialise MRPIS Metadata according to MPRIS_METADATA_MAP"""
@@ -389,8 +391,8 @@
                                               var=name,
                                               value=value))
 
-    @defer.inlineCallbacks
-    def local_media_cb(self, client, command_elt, session_data, action, node):
+    async def local_media_cb(self, client, command_elt, session_data, action, node):
+        assert txdbus is not None
         try:
             x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
             command_form = data_form.Form.fromElement(x_elt)
@@ -399,11 +401,11 @@
 
         if command_form is None or len(command_form.fields) == 0:
             # root request, we looks for media players
-            bus_names = yield self._dbus_list_names()
+            bus_names = await self._dbus_list_names()
             bus_names = [b for b in bus_names if b.startswith(MPRIS_PREFIX)]
             if len(bus_names) == 0:
                 note = (self._c.NOTE.INFO, D_("No media player found."))
-                defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
+                return (None, self._c.STATUS.COMPLETED, None, note)
             options = []
             status = self._c.STATUS.EXECUTING
             form = data_form.Form("form", title=D_("Media Player Selection"),
@@ -419,7 +421,7 @@
             )
             form.addField(field)
             payload = form.toElement()
-            defer.returnValue((payload, status, None, None))
+            return (payload, status, None, None)
         else:
             # player request
             try:
@@ -432,20 +434,22 @@
                               "Hack attempt? Refused bus: {bus_name}").format(
                               bus_name=bus_name))
                 note = (self._c.NOTE.ERROR, D_("Invalid player name."))
-                defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
+                return (None, self._c.STATUS.COMPLETED, None, note)
 
             try:
-                proxy = self.session_bus.get_object(bus_name, MPRIS_PATH)
-            except dbus.exceptions.DBusException as e:
+                proxy = await self.session_con.getRemoteObject(
+                    bus_name, MPRIS_PATH, "org.mpris.MediaPlayer2.Player"
+                )
+            except Exception as e:
                 log.warning(_("Can't get D-Bus proxy: {reason}").format(reason=e))
                 note = (self._c.NOTE.ERROR, D_("Media player is not available anymore"))
-                defer.returnValue((None, self._c.STATUS.COMPLETED, None, note))
+                return (None, self._c.STATUS.COMPLETED, None, note)
             try:
                 command = command_form["command"]
             except KeyError:
                 pass
             else:
-                yield self.do_mpris_command(proxy, command)
+                await self.do_mpris_command(proxy, command)
 
             # we construct the remote control form
             form = data_form.Form("form", title=D_("Media Player Selection"))
@@ -455,7 +459,7 @@
             for iface, properties_names in MPRIS_PROPERTIES.items():
                 for name in properties_names:
                     try:
-                        value = yield self._dbus_get_property(proxy, iface, name)
+                        value = await self._dbus_get_property(proxy, iface, name)
                     except Exception as e:
                         log.warning(_("Can't retrieve attribute {name}: {reason}")
                                     .format(name=name, reason=e))
@@ -475,4 +479,4 @@
 
             payload = form.toElement()
             status = self._c.STATUS.EXECUTING
-            defer.returnValue((payload, status, None, None))
+            return (payload, status, None, None)
--- a/libervia/cli/cmd_adhoc.py	Thu Nov 30 13:23:53 2023 +0100
+++ b/libervia/cli/cmd_adhoc.py	Fri Dec 01 15:22:55 2023 +0100
@@ -117,7 +117,7 @@
             "-j",
             "--jid",
             default="",
-            help=_("jid of the service (default: profile's server"),
+            help=_("jid of the service (default: profile's server)"),
         )
         self.parser.add_argument(
             "-S",