changeset 3539:60d3861e5996

bridge (dbus): use Tx DBus for backend part of D-Bus bridge: Due to recent SQLAlchemy integration, Libervia is now using AsyncIO loop exclusively as main loop, thus GLib's one can't be used anymore (event if it could be in a separate thread). Furthermore Python-DBus is known to have design flaws mentioned even in the official documentation. Tx DBus is now used to replace Python-DBus, but only for the backend for now, as it will need some work on the frontend before we can get completely rid of it.
author Goffi <goffi@goffi.org>
date Thu, 03 Jun 2021 15:21:43 +0200 (2021-06-03)
parents c605a0d6506f
children aa58451b77ba
files sat/bridge/bridge_constructor/constructors/dbus/constructor.py sat/bridge/bridge_constructor/constructors/dbus/dbus_core_template.py sat/bridge/dbus_bridge.py sat/core/sat_main.py sat_frontends/bridge/bridge_frontend.py setup.py
diffstat 6 files changed, 455 insertions(+), 898 deletions(-) [+]
line wrap: on
line diff
--- a/sat/bridge/bridge_constructor/constructors/dbus/constructor.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/sat/bridge/bridge_constructor/constructors/dbus/constructor.py	Thu Jun 03 15:21:43 2021 +0200
@@ -25,20 +25,19 @@
     CORE_TEMPLATE = "dbus_core_template.py"
     CORE_DEST = "dbus_bridge.py"
     CORE_FORMATS = {
-        "signals": """\
-    @dbus.service.signal(const_INT_PREFIX+const_{category}_SUFFIX,
-                         signature='{sig_in}')
-    def {name}(self, {args}):
-        {body}\n""",
+        "methods_declarations": """\
+        Method('{name}', arguments='{sig_in}', returns='{sig_out}'),""",
+
         "methods": """\
-    @dbus.service.method(const_INT_PREFIX+const_{category}_SUFFIX,
-                         in_signature='{sig_in}', out_signature='{sig_out}',
-                         async_callbacks={async_callbacks})
-    def {name}(self, {args}{async_comma}{async_args_def}):
-        {debug}return self._callback("{name}", {args_result}{async_comma}{async_args_call})\n""",
-        "signal_direct_calls": """\
+    def dbus_{name}(self, {args}):
+        {debug}return self._callback("{name}", {args_no_default})\n""",
+
+        "signals_declarations": """\
+        Signal('{name}', '{sig_in}'),""",
+
+        "signals": """\
     def {name}(self, {args}):
-        self.dbus_bridge.{name}({args})\n""",
+        self._obj.emitSignal("{name}", {args})\n""",
     }
 
     FRONTEND_TEMPLATE = "dbus_frontend_template.py"
@@ -68,17 +67,10 @@
     def core_completion_method(self, completion, function, default, arg_doc, async_):
         completion.update(
             {
-                "debug": ""
-                if not self.args.debug
-                else 'log.debug ("%s")\n%s' % (completion["name"], 8 * " "),
-                "args_result": self.getArguments(
-                    function["sig_in"], name=arg_doc, unicode_protect=self.args.unicode
-                ),
-                "async_comma": ", " if async_ and function["sig_in"] else "",
-                "async_args_def": "callback=None, errback=None" if async_ else "",
-                "async_args_call": "callback=callback, errback=errback" if async_ else "",
-                "async_callbacks": "('callback', 'errback')" if async_ else "None",
-                "category": completion["category"].upper(),
+                "debug": (
+                    "" if not self.args.debug
+                    else f'log.debug ("{completion["name"]}")\n{8 * " "}'
+                )
             }
         )
 
--- a/sat/bridge/bridge_constructor/constructors/dbus/dbus_core_template.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/sat/bridge/bridge_constructor/constructors/dbus/dbus_core_template.py	Thu Jun 03 15:21:43 2021 +0200
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# SàT communication bridge
+# Libervia communication bridge
 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
 
 # This program is free software: you can redistribute it and/or modify
@@ -16,15 +16,15 @@
 # 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 types import MethodType
+from functools import partialmethod
+from twisted.internet import defer, reactor
 from sat.core.i18n import _
-import dbus
-import dbus.service
-import dbus.mainloop.glib
-import inspect
 from sat.core.log import getLogger
+from sat.core.exceptions import BridgeInitError
 from sat.tools import config
-from twisted.internet.defer import Deferred
-from sat.core.exceptions import BridgeInitError
+from txdbus import client, objects, error
+from txdbus.interface import DBusInterface, Method, Signal
 
 
 log = getLogger(__name__)
@@ -45,251 +45,127 @@
     pass
 
 
-class MethodNotRegistered(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".MethodNotRegistered"
-
-
-class InternalError(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".InternalError"
+class DBusException(Exception):
+    pass
 
 
-class AsyncNotDeferred(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".AsyncNotDeferred"
+class MethodNotRegistered(DBusException):
+    dbusErrorName = const_ERROR_PREFIX + ".MethodNotRegistered"
 
 
-class DeferredNotAsync(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".DeferredNotAsync"
-
-
-class GenericException(dbus.DBusException):
+class GenericException(DBusException):
     def __init__(self, twisted_error):
         """
 
         @param twisted_error (Failure): instance of twisted Failure
-        @return: DBusException
+        error message is used to store a repr of message and condition in a tuple,
+        so it can be evaluated by the frontend bridge.
         """
-        super(GenericException, self).__init__()
         try:
             # twisted_error.value is a class
             class_ = twisted_error.value().__class__
         except TypeError:
             # twisted_error.value is an instance
             class_ = twisted_error.value.__class__
-            message = twisted_error.getErrorMessage()
+            data = twisted_error.getErrorMessage()
             try:
-                self.args = (message, twisted_error.value.condition)
+                data = (data, twisted_error.value.condition)
             except AttributeError:
-                self.args = (message,)
-        self._dbus_error_name = ".".join(
-            [const_ERROR_PREFIX, class_.__module__, class_.__name__]
+                data = (data,)
+        else:
+            data = (str(twisted_error),)
+        self.dbusErrorName = ".".join(
+            (const_ERROR_PREFIX, class_.__module__, class_.__name__)
         )
+        super(GenericException, self).__init__(repr(data))
+
+    @classmethod
+    def create_and_raise(cls, exc):
+        raise cls(exc)
 
 
-class DbusObject(dbus.service.Object):
-    def __init__(self, bus, path):
-        dbus.service.Object.__init__(self, bus, path)
-        log.debug("Init DbusObject...")
+class DBusObject(objects.DBusObject):
+
+    core_iface = DBusInterface(
+        const_INT_PREFIX + const_CORE_SUFFIX,
+##METHODS_DECLARATIONS_PART##
+##SIGNALS_DECLARATIONS_PART##
+    )
+    plugin_iface = DBusInterface(
+        const_INT_PREFIX + const_PLUGIN_SUFFIX
+    )
+
+    dbusInterfaces = [core_iface, plugin_iface]
+
+    def __init__(self, path):
+        super().__init__(path)
+        log.debug("Init DBusObject...")
         self.cb = {}
 
     def register_method(self, name, cb):
         self.cb[name] = cb
 
     def _callback(self, name, *args, **kwargs):
-        """call the callback if it exists, raise an exception else
-        if the callback return a deferred, use async methods"""
-        if not name in self.cb:
+        """Call the callback if it exists, raise an exception else"""
+        try:
+            cb = self.cb[name]
+        except KeyError:
             raise MethodNotRegistered
-
-        if "callback" in kwargs:
-            # we must have errback too
-            if not "errback" in kwargs:
-                log.error("errback is missing in method call [%s]" % name)
-                raise InternalError
-            callback = kwargs.pop("callback")
-            errback = kwargs.pop("errback")
-            async_ = True
         else:
-            async_ = False
-        result = self.cb[name](*args, **kwargs)
-        if async_:
-            if not isinstance(result, Deferred):
-                log.error("Asynchronous method [%s] does not return a Deferred." % name)
-                raise AsyncNotDeferred
-            result.addCallback(
-                lambda result: callback() if result is None else callback(result)
-            )
-            result.addErrback(lambda err: errback(GenericException(err)))
-        else:
-            if isinstance(result, Deferred):
-                log.error("Synchronous method [%s] return a Deferred." % name)
-                raise DeferredNotAsync
-            return result
-
-    ### signals ###
-
-    @dbus.service.signal(const_INT_PREFIX + const_PLUGIN_SUFFIX, signature="")
-    def dummySignal(self):
-        # FIXME: workaround for addSignal (doesn't work if one method doensn't
-        #       already exist for plugins), probably missing some initialisation, need
-        #       further investigations
-        pass
-
-##SIGNALS_PART##
-    ### methods ###
+            d = defer.maybeDeferred(cb, *args, **kwargs)
+            d.addErrback(GenericException.create_and_raise)
+            return d
 
 ##METHODS_PART##
-    def __attributes(self, in_sign):
-        """Return arguments to user given a in_sign
-        @param in_sign: in_sign in the short form (using s,a,i,b etc)
-        @return: list of arguments that correspond to a in_sign (e.g.: "sss" return "arg1, arg2, arg3")"""
-        i = 0
-        idx = 0
-        attr = []
-        while i < len(in_sign):
-            if in_sign[i] not in ["b", "y", "n", "i", "x", "q", "u", "t", "d", "s", "a"]:
-                raise ParseError("Unmanaged attribute type [%c]" % in_sign[i])
 
-            attr.append("arg_%i" % idx)
-            idx += 1
-
-            if in_sign[i] == "a":
-                i += 1
-                if (
-                    in_sign[i] != "{" and in_sign[i] != "("
-                ):  # FIXME: must manage tuples out of arrays
-                    i += 1
-                    continue  # we have a simple type for the array
-                opening_car = in_sign[i]
-                assert opening_car in ["{", "("]
-                closing_car = "}" if opening_car == "{" else ")"
-                opening_count = 1
-                while True:  # we have a dict or a list of tuples
-                    i += 1
-                    if i >= len(in_sign):
-                        raise ParseError("missing }")
-                    if in_sign[i] == opening_car:
-                        opening_count += 1
-                    if in_sign[i] == closing_car:
-                        opening_count -= 1
-                        if opening_count == 0:
-                            break
-            i += 1
-        return attr
-
-    def addMethod(self, name, int_suffix, in_sign, out_sign, method, async_=False):
-        """Dynamically add a method to Dbus Bridge"""
-        inspect_args = inspect.getfullargspec(method)
-
-        _arguments = inspect_args.args
-        _defaults = list(inspect_args.defaults or [])
-
-        if inspect.ismethod(method):
-            # if we have a method, we don't want the first argument (usually 'self')
-            del (_arguments[0])
-
-        # first arguments are for the _callback method
-        arguments_callback = ", ".join(
-            [repr(name)]
-            + (
-                (_arguments + ["callback=callback", "errback=errback"])
-                if async_
-                else _arguments
-            )
-        )
-
-        if async_:
-            _arguments.extend(["callback", "errback"])
-            _defaults.extend([None, None])
+class Bridge:
 
-        # now we create a second list with default values
-        for i in range(1, len(_defaults) + 1):
-            _arguments[-i] = "%s = %s" % (_arguments[-i], repr(_defaults[-i]))
-
-        arguments_defaults = ", ".join(_arguments)
+    def __init__(self):
+        log.info("Init DBus...")
+        self._obj = DBusObject(const_OBJ_PATH)
 
-        code = compile(
-            "def %(name)s (self,%(arguments_defaults)s): return self._callback(%(arguments_callback)s)"
-            % {
-                "name": name,
-                "arguments_defaults": arguments_defaults,
-                "arguments_callback": arguments_callback,
-            },
-            "<DBus bridge>",
-            "exec",
-        )
-        exec(code)  # FIXME: to the same thing in a cleaner way, without compile/exec
-        method = locals()[name]
-        async_callbacks = ("callback", "errback") if async_ else None
-        setattr(
-            DbusObject,
-            name,
-            dbus.service.method(
-                const_INT_PREFIX + int_suffix,
-                in_signature=in_sign,
-                out_signature=out_sign,
-                async_callbacks=async_callbacks,
-            )(method),
-        )
-        function = getattr(self, name)
-        func_table = self._dbus_class_table[
-            self.__class__.__module__ + "." + self.__class__.__name__
-        ][function._dbus_interface]
-        func_table[function.__name__] = function  # Needed for introspection
-
-    def addSignal(self, name, int_suffix, signature, doc={}):
-        """Dynamically add a signal to Dbus Bridge"""
-        attributes = ", ".join(self.__attributes(signature))
-        # TODO: use doc parameter to name attributes
-
-        # code = compile ('def '+name+' (self,'+attributes+'): log.debug ("'+name+' signal")', '<DBus bridge>','exec') #XXX: the log.debug is too annoying with xmllog
-        code = compile(
-            "def " + name + " (self," + attributes + "): pass", "<DBus bridge>", "exec"
-        )
-        exec(code)
-        signal = locals()[name]
-        setattr(
-            DbusObject,
-            name,
-            dbus.service.signal(const_INT_PREFIX + int_suffix, signature=signature)(
-                signal
-            ),
-        )
-        function = getattr(self, name)
-        func_table = self._dbus_class_table[
-            self.__class__.__module__ + "." + self.__class__.__name__
-        ][function._dbus_interface]
-        func_table[function.__name__] = function  # Needed for introspection
-
-
-class Bridge(object):
-    def __init__(self):
-        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-        log.info("Init DBus...")
+    async def postInit(self):
         try:
-            self.session_bus = dbus.SessionBus()
-        except dbus.DBusException as e:
-            if e._dbus_error_name == "org.freedesktop.DBus.Error.NotSupported":
+            conn = await client.connect(reactor)
+        except error.DBusException as e:
+            if e.errName == "org.freedesktop.DBus.Error.NotSupported":
                 log.error(
                     _(
-                        "D-Bus is not launched, please see README to see instructions on how to launch it"
+                        "D-Bus is not launched, please see README to see instructions on "
+                        "how to launch it"
                     )
                 )
-            raise BridgeInitError
-        self.dbus_name = dbus.service.BusName(const_INT_PREFIX, self.session_bus)
-        self.dbus_bridge = DbusObject(self.session_bus, const_OBJ_PATH)
+            raise BridgeInitError(str(e))
 
-##SIGNAL_DIRECT_CALLS_PART##
+        conn.exportObject(self._obj)
+        await conn.requestBusName(const_INT_PREFIX)
+
+##SIGNALS_PART##
     def register_method(self, name, callback):
-        log.debug("registering DBus bridge method [%s]" % name)
-        self.dbus_bridge.register_method(name, callback)
+        log.debug(f"registering DBus bridge method [{name}]")
+        self._obj.register_method(name, callback)
+
+    def emitSignal(self, name, *args):
+        self._obj.emitSignal(name, *args)
 
-    def addMethod(self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={}):
-        """Dynamically add a method to Dbus Bridge"""
+    def addMethod(
+            self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={}
+    ):
+        """Dynamically add a method to D-Bus Bridge"""
         # FIXME: doc parameter is kept only temporary, the time to remove it from calls
-        log.debug("Adding method [%s] to DBus bridge" % name)
-        self.dbus_bridge.addMethod(name, int_suffix, in_sign, out_sign, method, async_)
+        log.debug(f"Adding method {name!r} to D-Bus bridge")
+        self._obj.plugin_iface.addMethod(
+            Method(name, arguments=in_sign, returns=out_sign)
+        )
+        # we have to create a method here instead of using partialmethod, because txdbus
+        # uses __func__ which doesn't work with partialmethod
+        def caller(self_, *args, **kwargs):
+            return self_._callback(name, *args, **kwargs)
+        setattr(self._obj, f"dbus_{name}", MethodType(caller, self._obj))
         self.register_method(name, method)
 
     def addSignal(self, name, int_suffix, signature, doc={}):
-        self.dbus_bridge.addSignal(name, int_suffix, signature, doc)
-        setattr(Bridge, name, getattr(self.dbus_bridge, name))
+        """Dynamically add a signal to D-Bus Bridge"""
+        log.debug(f"Adding signal {name!r} to D-Bus bridge")
+        self._obj.plugin_iface.addSignal(Signal(name, signature))
+        setattr(Bridge, name, partialmethod(Bridge.emitSignal, name))
--- a/sat/bridge/dbus_bridge.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/sat/bridge/dbus_bridge.py	Thu Jun 03 15:21:43 2021 +0200
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# SàT communication bridge
+# Libervia communication bridge
 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
 
 # This program is free software: you can redistribute it and/or modify
@@ -16,15 +16,15 @@
 # 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 types import MethodType
+from functools import partialmethod
+from twisted.internet import defer, reactor
 from sat.core.i18n import _
-import dbus
-import dbus.service
-import dbus.mainloop.glib
-import inspect
 from sat.core.log import getLogger
+from sat.core.exceptions import BridgeInitError
 from sat.tools import config
-from twisted.internet.defer import Deferred
-from sat.core.exceptions import BridgeInitError
+from txdbus import client, objects, error
+from txdbus.interface import DBusInterface, Method, Signal
 
 
 log = getLogger(__name__)
@@ -45,772 +45,451 @@
     pass
 
 
-class MethodNotRegistered(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".MethodNotRegistered"
-
-
-class InternalError(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".InternalError"
+class DBusException(Exception):
+    pass
 
 
-class AsyncNotDeferred(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".AsyncNotDeferred"
+class MethodNotRegistered(DBusException):
+    dbusErrorName = const_ERROR_PREFIX + ".MethodNotRegistered"
 
 
-class DeferredNotAsync(dbus.DBusException):
-    _dbus_error_name = const_ERROR_PREFIX + ".DeferredNotAsync"
-
-
-class GenericException(dbus.DBusException):
+class GenericException(DBusException):
     def __init__(self, twisted_error):
         """
 
         @param twisted_error (Failure): instance of twisted Failure
-        @return: DBusException
+        error message is used to store a repr of message and condition in a tuple,
+        so it can be evaluated by the frontend bridge.
         """
-        super(GenericException, self).__init__()
         try:
             # twisted_error.value is a class
             class_ = twisted_error.value().__class__
         except TypeError:
             # twisted_error.value is an instance
             class_ = twisted_error.value.__class__
-            message = twisted_error.getErrorMessage()
+            data = twisted_error.getErrorMessage()
             try:
-                self.args = (message, twisted_error.value.condition)
+                data = (data, twisted_error.value.condition)
             except AttributeError:
-                self.args = (message,)
-        self._dbus_error_name = ".".join(
-            [const_ERROR_PREFIX, class_.__module__, class_.__name__]
+                data = (data,)
+        else:
+            data = (str(twisted_error),)
+        self.dbusErrorName = ".".join(
+            (const_ERROR_PREFIX, class_.__module__, class_.__name__)
         )
+        super(GenericException, self).__init__(repr(data))
+
+    @classmethod
+    def create_and_raise(cls, exc):
+        raise cls(exc)
 
 
-class DbusObject(dbus.service.Object):
-    def __init__(self, bus, path):
-        dbus.service.Object.__init__(self, bus, path)
-        log.debug("Init DbusObject...")
+class DBusObject(objects.DBusObject):
+
+    core_iface = DBusInterface(
+        const_INT_PREFIX + const_CORE_SUFFIX,
+        Method('actionsGet', arguments='s', returns='a(a{ss}si)'),
+        Method('addContact', arguments='ss', returns=''),
+        Method('asyncDeleteProfile', arguments='s', returns=''),
+        Method('asyncGetParamA', arguments='sssis', returns='s'),
+        Method('asyncGetParamsValuesFromCategory', arguments='sisss', returns='a{ss}'),
+        Method('connect', arguments='ssa{ss}', returns='b'),
+        Method('contactGet', arguments='ss', returns='(a{ss}as)'),
+        Method('delContact', arguments='ss', returns=''),
+        Method('devicesInfosGet', arguments='ss', returns='s'),
+        Method('discoFindByFeatures', arguments='asa(ss)bbbbbs', returns='(a{sa(sss)}a{sa(sss)}a{sa(sss)})'),
+        Method('discoInfos', arguments='ssbs', returns='(asa(sss)a{sa(a{ss}as)})'),
+        Method('discoItems', arguments='ssbs', returns='a(sss)'),
+        Method('disconnect', arguments='s', returns=''),
+        Method('encryptionNamespaceGet', arguments='s', returns='s'),
+        Method('encryptionPluginsGet', arguments='', returns='s'),
+        Method('encryptionTrustUIGet', arguments='sss', returns='s'),
+        Method('getConfig', arguments='ss', returns='s'),
+        Method('getContacts', arguments='s', returns='a(sa{ss}as)'),
+        Method('getContactsFromGroup', arguments='ss', returns='as'),
+        Method('getEntitiesData', arguments='asass', returns='a{sa{ss}}'),
+        Method('getEntityData', arguments='sass', returns='a{ss}'),
+        Method('getFeatures', arguments='s', returns='a{sa{ss}}'),
+        Method('getMainResource', arguments='ss', returns='s'),
+        Method('getParamA', arguments='ssss', returns='s'),
+        Method('getParamsCategories', arguments='', returns='as'),
+        Method('getParamsUI', arguments='isss', returns='s'),
+        Method('getPresenceStatuses', arguments='s', returns='a{sa{s(sia{ss})}}'),
+        Method('getReady', arguments='', returns=''),
+        Method('getVersion', arguments='', returns='s'),
+        Method('getWaitingSub', arguments='s', returns='a{ss}'),
+        Method('historyGet', arguments='ssiba{ss}s', returns='a(sdssa{ss}a{ss}ss)'),
+        Method('imageCheck', arguments='s', returns='s'),
+        Method('imageConvert', arguments='ssss', returns='s'),
+        Method('imageGeneratePreview', arguments='ss', returns='s'),
+        Method('imageResize', arguments='sii', returns='s'),
+        Method('isConnected', arguments='s', returns='b'),
+        Method('launchAction', arguments='sa{ss}s', returns='a{ss}'),
+        Method('loadParamsTemplate', arguments='s', returns='b'),
+        Method('menuHelpGet', arguments='ss', returns='s'),
+        Method('menuLaunch', arguments='sasa{ss}is', returns='a{ss}'),
+        Method('menusGet', arguments='si', returns='a(ssasasa{ss})'),
+        Method('messageEncryptionGet', arguments='ss', returns='s'),
+        Method('messageEncryptionStart', arguments='ssbs', returns=''),
+        Method('messageEncryptionStop', arguments='ss', returns=''),
+        Method('messageSend', arguments='sa{ss}a{ss}sss', returns=''),
+        Method('namespacesGet', arguments='', returns='a{ss}'),
+        Method('paramsRegisterApp', arguments='sis', returns=''),
+        Method('privateDataDelete', arguments='sss', returns=''),
+        Method('privateDataGet', arguments='sss', returns='s'),
+        Method('privateDataSet', arguments='ssss', returns=''),
+        Method('profileCreate', arguments='sss', returns=''),
+        Method('profileIsSessionStarted', arguments='s', returns='b'),
+        Method('profileNameGet', arguments='s', returns='s'),
+        Method('profileSetDefault', arguments='s', returns=''),
+        Method('profileStartSession', arguments='ss', returns='b'),
+        Method('profilesListGet', arguments='bb', returns='as'),
+        Method('progressGet', arguments='ss', returns='a{ss}'),
+        Method('progressGetAll', arguments='s', returns='a{sa{sa{ss}}}'),
+        Method('progressGetAllMetadata', arguments='s', returns='a{sa{sa{ss}}}'),
+        Method('rosterResync', arguments='s', returns=''),
+        Method('saveParamsTemplate', arguments='s', returns='b'),
+        Method('sessionInfosGet', arguments='s', returns='a{ss}'),
+        Method('setParam', arguments='sssis', returns=''),
+        Method('setPresence', arguments='ssa{ss}s', returns=''),
+        Method('subscription', arguments='sss', returns=''),
+        Method('updateContact', arguments='ssass', returns=''),
+        Signal('_debug', 'sa{ss}s'),
+        Signal('actionNew', 'a{ss}sis'),
+        Signal('connected', 'ss'),
+        Signal('contactDeleted', 'ss'),
+        Signal('disconnected', 's'),
+        Signal('entityDataUpdated', 'ssss'),
+        Signal('messageEncryptionStarted', 'sss'),
+        Signal('messageEncryptionStopped', 'sa{ss}s'),
+        Signal('messageNew', 'sdssa{ss}a{ss}sss'),
+        Signal('newContact', 'sa{ss}ass'),
+        Signal('paramUpdate', 'ssss'),
+        Signal('presenceUpdate', 'ssia{ss}s'),
+        Signal('progressError', 'sss'),
+        Signal('progressFinished', 'sa{ss}s'),
+        Signal('progressStarted', 'sa{ss}s'),
+        Signal('subscribe', 'sss'),
+    )
+    plugin_iface = DBusInterface(
+        const_INT_PREFIX + const_PLUGIN_SUFFIX
+    )
+
+    dbusInterfaces = [core_iface, plugin_iface]
+
+    def __init__(self, path):
+        super().__init__(path)
+        log.debug("Init DBusObject...")
         self.cb = {}
 
     def register_method(self, name, cb):
         self.cb[name] = cb
 
     def _callback(self, name, *args, **kwargs):
-        """call the callback if it exists, raise an exception else
-        if the callback return a deferred, use async methods"""
-        if not name in self.cb:
+        """Call the callback if it exists, raise an exception else"""
+        try:
+            cb = self.cb[name]
+        except KeyError:
             raise MethodNotRegistered
-
-        if "callback" in kwargs:
-            # we must have errback too
-            if not "errback" in kwargs:
-                log.error("errback is missing in method call [%s]" % name)
-                raise InternalError
-            callback = kwargs.pop("callback")
-            errback = kwargs.pop("errback")
-            async_ = True
         else:
-            async_ = False
-        result = self.cb[name](*args, **kwargs)
-        if async_:
-            if not isinstance(result, Deferred):
-                log.error("Asynchronous method [%s] does not return a Deferred." % name)
-                raise AsyncNotDeferred
-            result.addCallback(
-                lambda result: callback() if result is None else callback(result)
-            )
-            result.addErrback(lambda err: errback(GenericException(err)))
-        else:
-            if isinstance(result, Deferred):
-                log.error("Synchronous method [%s] return a Deferred." % name)
-                raise DeferredNotAsync
-            return result
-
-    ### signals ###
-
-    @dbus.service.signal(const_INT_PREFIX + const_PLUGIN_SUFFIX, signature="")
-    def dummySignal(self):
-        # FIXME: workaround for addSignal (doesn't work if one method doensn't
-        #       already exist for plugins), probably missing some initialisation, need
-        #       further investigations
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sa{ss}s')
-    def _debug(self, action, params, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='a{ss}sis')
-    def actionNew(self, action_data, id, security_limit, profile):
-        pass
+            d = defer.maybeDeferred(cb, *args, **kwargs)
+            d.addErrback(GenericException.create_and_raise)
+            return d
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='ss')
-    def connected(self, jid_s, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='ss')
-    def contactDeleted(self, entity_jid, profile):
-        pass
+    def dbus_actionsGet(self, profile_key="@DEFAULT@"):
+        return self._callback("actionsGet", profile_key)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='s')
-    def disconnected(self, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='ssss')
-    def entityDataUpdated(self, jid, name, value, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sss')
-    def messageEncryptionStarted(self, to_jid, encryption_data, profile_key):
-        pass
+    def dbus_addContact(self, entity_jid, profile_key="@DEFAULT@"):
+        return self._callback("addContact", entity_jid, profile_key)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sa{ss}s')
-    def messageEncryptionStopped(self, to_jid, encryption_data, profile_key):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sdssa{ss}a{ss}sss')
-    def messageNew(self, uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sa{ss}ass')
-    def newContact(self, contact_jid, attributes, groups, profile):
-        pass
+    def dbus_asyncDeleteProfile(self, profile):
+        return self._callback("asyncDeleteProfile", profile)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='ssss')
-    def paramUpdate(self, name, value, category, profile):
-        pass
+    def dbus_asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@"):
+        return self._callback("asyncGetParamA", name, category, attribute, security_limit, profile_key)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='ssia{ss}s')
-    def presenceUpdate(self, entity_jid, show, priority, statuses, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sss')
-    def progressError(self, id, error, profile):
-        pass
+    def dbus_asyncGetParamsValuesFromCategory(self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"):
+        return self._callback("asyncGetParamsValuesFromCategory", category, security_limit, app, extra, profile_key)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sa{ss}s')
-    def progressFinished(self, id, metadata, profile):
-        pass
-
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sa{ss}s')
-    def progressStarted(self, id, metadata, profile):
-        pass
+    def dbus_connect(self, profile_key="@DEFAULT@", password='', options={}):
+        return self._callback("connect", profile_key, password, options)
 
-    @dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
-                         signature='sss')
-    def subscribe(self, sub_type, entity_jid, profile):
-        pass
-
-    ### methods ###
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a(a{ss}si)',
-                         async_callbacks=None)
-    def actionsGet(self, profile_key="@DEFAULT@"):
-        return self._callback("actionsGet", str(profile_key))
+    def dbus_contactGet(self, arg_0, profile_key="@DEFAULT@"):
+        return self._callback("contactGet", arg_0, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='',
-                         async_callbacks=None)
-    def addContact(self, entity_jid, profile_key="@DEFAULT@"):
-        return self._callback("addContact", str(entity_jid), str(profile_key))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def asyncDeleteProfile(self, profile, callback=None, errback=None):
-        return self._callback("asyncDeleteProfile", str(profile), callback=callback, errback=errback)
+    def dbus_delContact(self, entity_jid, profile_key="@DEFAULT@"):
+        return self._callback("delContact", entity_jid, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sssis', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("asyncGetParamA", str(name), str(category), str(attribute), security_limit, str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sisss', out_signature='a{ss}',
-                         async_callbacks=('callback', 'errback'))
-    def asyncGetParamsValuesFromCategory(self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("asyncGetParamsValuesFromCategory", str(category), security_limit, str(app), str(extra), str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssa{ss}', out_signature='b',
-                         async_callbacks=('callback', 'errback'))
-    def connect(self, profile_key="@DEFAULT@", password='', options={}, callback=None, errback=None):
-        return self._callback("connect", str(profile_key), str(password), options, callback=callback, errback=errback)
+    def dbus_devicesInfosGet(self, bare_jid, profile_key):
+        return self._callback("devicesInfosGet", bare_jid, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='(a{ss}as)',
-                         async_callbacks=('callback', 'errback'))
-    def contactGet(self, arg_0, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("contactGet", str(arg_0), str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def delContact(self, entity_jid, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("delContact", str(entity_jid), str(profile_key), callback=callback, errback=errback)
+    def dbus_discoFindByFeatures(self, namespaces, identities, bare_jid=False, service=True, roster=True, own_jid=True, local_device=False, profile_key="@DEFAULT@"):
+        return self._callback("discoFindByFeatures", namespaces, identities, bare_jid, service, roster, own_jid, local_device, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def devicesInfosGet(self, bare_jid, profile_key, callback=None, errback=None):
-        return self._callback("devicesInfosGet", str(bare_jid), str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='asa(ss)bbbbbs', out_signature='(a{sa(sss)}a{sa(sss)}a{sa(sss)})',
-                         async_callbacks=('callback', 'errback'))
-    def discoFindByFeatures(self, namespaces, identities, bare_jid=False, service=True, roster=True, own_jid=True, local_device=False, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("discoFindByFeatures", namespaces, identities, bare_jid, service, roster, own_jid, local_device, str(profile_key), callback=callback, errback=errback)
+    def dbus_discoInfos(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"):
+        return self._callback("discoInfos", entity_jid, node, use_cache, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssbs', out_signature='(asa(sss)a{sa(a{ss}as)})',
-                         async_callbacks=('callback', 'errback'))
-    def discoInfos(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("discoInfos", str(entity_jid), str(node), use_cache, str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssbs', out_signature='a(sss)',
-                         async_callbacks=('callback', 'errback'))
-    def discoItems(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("discoItems", str(entity_jid), str(node), use_cache, str(profile_key), callback=callback, errback=errback)
+    def dbus_discoItems(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"):
+        return self._callback("discoItems", entity_jid, node, use_cache, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def disconnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("disconnect", str(profile_key), callback=callback, errback=errback)
+    def dbus_disconnect(self, profile_key="@DEFAULT@"):
+        return self._callback("disconnect", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='s',
-                         async_callbacks=None)
-    def encryptionNamespaceGet(self, arg_0):
-        return self._callback("encryptionNamespaceGet", str(arg_0))
+    def dbus_encryptionNamespaceGet(self, arg_0):
+        return self._callback("encryptionNamespaceGet", arg_0)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='', out_signature='s',
-                         async_callbacks=None)
-    def encryptionPluginsGet(self, ):
+    def dbus_encryptionPluginsGet(self, ):
         return self._callback("encryptionPluginsGet", )
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def encryptionTrustUIGet(self, to_jid, namespace, profile_key, callback=None, errback=None):
-        return self._callback("encryptionTrustUIGet", str(to_jid), str(namespace), str(profile_key), callback=callback, errback=errback)
+    def dbus_encryptionTrustUIGet(self, to_jid, namespace, profile_key):
+        return self._callback("encryptionTrustUIGet", to_jid, namespace, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=None)
-    def getConfig(self, section, name):
-        return self._callback("getConfig", str(section), str(name))
+    def dbus_getConfig(self, section, name):
+        return self._callback("getConfig", section, name)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a(sa{ss}as)',
-                         async_callbacks=('callback', 'errback'))
-    def getContacts(self, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("getContacts", str(profile_key), callback=callback, errback=errback)
+    def dbus_getContacts(self, profile_key="@DEFAULT@"):
+        return self._callback("getContacts", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='as',
-                         async_callbacks=None)
-    def getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
-        return self._callback("getContactsFromGroup", str(group), str(profile_key))
+    def dbus_getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
+        return self._callback("getContactsFromGroup", group, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='asass', out_signature='a{sa{ss}}',
-                         async_callbacks=None)
-    def getEntitiesData(self, jids, keys, profile):
-        return self._callback("getEntitiesData", jids, keys, str(profile))
+    def dbus_getEntitiesData(self, jids, keys, profile):
+        return self._callback("getEntitiesData", jids, keys, profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sass', out_signature='a{ss}',
-                         async_callbacks=None)
-    def getEntityData(self, jid, keys, profile):
-        return self._callback("getEntityData", str(jid), keys, str(profile))
+    def dbus_getEntityData(self, jid, keys, profile):
+        return self._callback("getEntityData", jid, keys, profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{sa{ss}}',
-                         async_callbacks=('callback', 'errback'))
-    def getFeatures(self, profile_key, callback=None, errback=None):
-        return self._callback("getFeatures", str(profile_key), callback=callback, errback=errback)
+    def dbus_getFeatures(self, profile_key):
+        return self._callback("getFeatures", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=None)
-    def getMainResource(self, contact_jid, profile_key="@DEFAULT@"):
-        return self._callback("getMainResource", str(contact_jid), str(profile_key))
+    def dbus_getMainResource(self, contact_jid, profile_key="@DEFAULT@"):
+        return self._callback("getMainResource", contact_jid, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssss', out_signature='s',
-                         async_callbacks=None)
-    def getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"):
-        return self._callback("getParamA", str(name), str(category), str(attribute), str(profile_key))
+    def dbus_getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"):
+        return self._callback("getParamA", name, category, attribute, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='', out_signature='as',
-                         async_callbacks=None)
-    def getParamsCategories(self, ):
+    def dbus_getParamsCategories(self, ):
         return self._callback("getParamsCategories", )
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='isss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def getParamsUI(self, security_limit=-1, app='', extra='', profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("getParamsUI", security_limit, str(app), str(extra), str(profile_key), callback=callback, errback=errback)
+    def dbus_getParamsUI(self, security_limit=-1, app='', extra='', profile_key="@DEFAULT@"):
+        return self._callback("getParamsUI", security_limit, app, extra, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{sa{s(sia{ss})}}',
-                         async_callbacks=None)
-    def getPresenceStatuses(self, profile_key="@DEFAULT@"):
-        return self._callback("getPresenceStatuses", str(profile_key))
+    def dbus_getPresenceStatuses(self, profile_key="@DEFAULT@"):
+        return self._callback("getPresenceStatuses", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def getReady(self, callback=None, errback=None):
-        return self._callback("getReady", callback=callback, errback=errback)
+    def dbus_getReady(self, ):
+        return self._callback("getReady", )
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='', out_signature='s',
-                         async_callbacks=None)
-    def getVersion(self, ):
+    def dbus_getVersion(self, ):
         return self._callback("getVersion", )
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{ss}',
-                         async_callbacks=None)
-    def getWaitingSub(self, profile_key="@DEFAULT@"):
-        return self._callback("getWaitingSub", str(profile_key))
+    def dbus_getWaitingSub(self, profile_key="@DEFAULT@"):
+        return self._callback("getWaitingSub", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssiba{ss}s', out_signature='a(sdssa{ss}a{ss}ss)',
-                         async_callbacks=('callback', 'errback'))
-    def historyGet(self, from_jid, to_jid, limit, between=True, filters='', profile="@NONE@", callback=None, errback=None):
-        return self._callback("historyGet", str(from_jid), str(to_jid), limit, between, filters, str(profile), callback=callback, errback=errback)
+    def dbus_historyGet(self, from_jid, to_jid, limit, between=True, filters='', profile="@NONE@"):
+        return self._callback("historyGet", from_jid, to_jid, limit, between, filters, profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='s',
-                         async_callbacks=None)
-    def imageCheck(self, arg_0):
-        return self._callback("imageCheck", str(arg_0))
+    def dbus_imageCheck(self, arg_0):
+        return self._callback("imageCheck", arg_0)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def imageConvert(self, source, dest, arg_2, extra, callback=None, errback=None):
-        return self._callback("imageConvert", str(source), str(dest), str(arg_2), str(extra), callback=callback, errback=errback)
+    def dbus_imageConvert(self, source, dest, arg_2, extra):
+        return self._callback("imageConvert", source, dest, arg_2, extra)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def imageGeneratePreview(self, image_path, profile_key, callback=None, errback=None):
-        return self._callback("imageGeneratePreview", str(image_path), str(profile_key), callback=callback, errback=errback)
+    def dbus_imageGeneratePreview(self, image_path, profile_key):
+        return self._callback("imageGeneratePreview", image_path, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sii', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def imageResize(self, image_path, width, height, callback=None, errback=None):
-        return self._callback("imageResize", str(image_path), width, height, callback=callback, errback=errback)
+    def dbus_imageResize(self, image_path, width, height):
+        return self._callback("imageResize", image_path, width, height)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='b',
-                         async_callbacks=None)
-    def isConnected(self, profile_key="@DEFAULT@"):
-        return self._callback("isConnected", str(profile_key))
+    def dbus_isConnected(self, profile_key="@DEFAULT@"):
+        return self._callback("isConnected", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sa{ss}s', out_signature='a{ss}',
-                         async_callbacks=('callback', 'errback'))
-    def launchAction(self, callback_id, data, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("launchAction", str(callback_id), data, str(profile_key), callback=callback, errback=errback)
+    def dbus_launchAction(self, callback_id, data, profile_key="@DEFAULT@"):
+        return self._callback("launchAction", callback_id, data, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='b',
-                         async_callbacks=None)
-    def loadParamsTemplate(self, filename):
-        return self._callback("loadParamsTemplate", str(filename))
+    def dbus_loadParamsTemplate(self, filename):
+        return self._callback("loadParamsTemplate", filename)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=None)
-    def menuHelpGet(self, menu_id, language):
-        return self._callback("menuHelpGet", str(menu_id), str(language))
+    def dbus_menuHelpGet(self, menu_id, language):
+        return self._callback("menuHelpGet", menu_id, language)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sasa{ss}is', out_signature='a{ss}',
-                         async_callbacks=('callback', 'errback'))
-    def menuLaunch(self, menu_type, path, data, security_limit, profile_key, callback=None, errback=None):
-        return self._callback("menuLaunch", str(menu_type), path, data, security_limit, str(profile_key), callback=callback, errback=errback)
+    def dbus_menuLaunch(self, menu_type, path, data, security_limit, profile_key):
+        return self._callback("menuLaunch", menu_type, path, data, security_limit, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='si', out_signature='a(ssasasa{ss})',
-                         async_callbacks=None)
-    def menusGet(self, language, security_limit):
-        return self._callback("menusGet", str(language), security_limit)
+    def dbus_menusGet(self, language, security_limit):
+        return self._callback("menusGet", language, security_limit)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='s',
-                         async_callbacks=None)
-    def messageEncryptionGet(self, to_jid, profile_key):
-        return self._callback("messageEncryptionGet", str(to_jid), str(profile_key))
+    def dbus_messageEncryptionGet(self, to_jid, profile_key):
+        return self._callback("messageEncryptionGet", to_jid, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssbs', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@", callback=None, errback=None):
-        return self._callback("messageEncryptionStart", str(to_jid), str(namespace), replace, str(profile_key), callback=callback, errback=errback)
+    def dbus_messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@"):
+        return self._callback("messageEncryptionStart", to_jid, namespace, replace, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def messageEncryptionStop(self, to_jid, profile_key, callback=None, errback=None):
-        return self._callback("messageEncryptionStop", str(to_jid), str(profile_key), callback=callback, errback=errback)
+    def dbus_messageEncryptionStop(self, to_jid, profile_key):
+        return self._callback("messageEncryptionStop", to_jid, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sa{ss}a{ss}sss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def messageSend(self, to_jid, message, subject={}, mess_type="auto", extra={}, profile_key="@NONE@", callback=None, errback=None):
-        return self._callback("messageSend", str(to_jid), message, subject, str(mess_type), str(extra), str(profile_key), callback=callback, errback=errback)
+    def dbus_messageSend(self, to_jid, message, subject={}, mess_type="auto", extra={}, profile_key="@NONE@"):
+        return self._callback("messageSend", to_jid, message, subject, mess_type, extra, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='', out_signature='a{ss}',
-                         async_callbacks=None)
-    def namespacesGet(self, ):
+    def dbus_namespacesGet(self, ):
         return self._callback("namespacesGet", )
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sis', out_signature='',
-                         async_callbacks=None)
-    def paramsRegisterApp(self, xml, security_limit=-1, app=''):
-        return self._callback("paramsRegisterApp", str(xml), security_limit, str(app))
+    def dbus_paramsRegisterApp(self, xml, security_limit=-1, app=''):
+        return self._callback("paramsRegisterApp", xml, security_limit, app)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def privateDataDelete(self, namespace, key, arg_2, callback=None, errback=None):
-        return self._callback("privateDataDelete", str(namespace), str(key), str(arg_2), callback=callback, errback=errback)
+    def dbus_privateDataDelete(self, namespace, key, arg_2):
+        return self._callback("privateDataDelete", namespace, key, arg_2)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sss', out_signature='s',
-                         async_callbacks=('callback', 'errback'))
-    def privateDataGet(self, namespace, key, profile_key, callback=None, errback=None):
-        return self._callback("privateDataGet", str(namespace), str(key), str(profile_key), callback=callback, errback=errback)
+    def dbus_privateDataGet(self, namespace, key, profile_key):
+        return self._callback("privateDataGet", namespace, key, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def privateDataSet(self, namespace, key, data, profile_key, callback=None, errback=None):
-        return self._callback("privateDataSet", str(namespace), str(key), str(data), str(profile_key), callback=callback, errback=errback)
+    def dbus_privateDataSet(self, namespace, key, data, profile_key):
+        return self._callback("privateDataSet", namespace, key, data, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sss', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def profileCreate(self, profile, password='', component='', callback=None, errback=None):
-        return self._callback("profileCreate", str(profile), str(password), str(component), callback=callback, errback=errback)
+    def dbus_profileCreate(self, profile, password='', component=''):
+        return self._callback("profileCreate", profile, password, component)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='b',
-                         async_callbacks=None)
-    def profileIsSessionStarted(self, profile_key="@DEFAULT@"):
-        return self._callback("profileIsSessionStarted", str(profile_key))
+    def dbus_profileIsSessionStarted(self, profile_key="@DEFAULT@"):
+        return self._callback("profileIsSessionStarted", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='s',
-                         async_callbacks=None)
-    def profileNameGet(self, profile_key="@DEFAULT@"):
-        return self._callback("profileNameGet", str(profile_key))
+    def dbus_profileNameGet(self, profile_key="@DEFAULT@"):
+        return self._callback("profileNameGet", profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='',
-                         async_callbacks=None)
-    def profileSetDefault(self, profile):
-        return self._callback("profileSetDefault", str(profile))
+    def dbus_profileSetDefault(self, profile):
+        return self._callback("profileSetDefault", profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='b',
-                         async_callbacks=('callback', 'errback'))
-    def profileStartSession(self, password='', profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("profileStartSession", str(password), str(profile_key), callback=callback, errback=errback)
+    def dbus_profileStartSession(self, password='', profile_key="@DEFAULT@"):
+        return self._callback("profileStartSession", password, profile_key)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='bb', out_signature='as',
-                         async_callbacks=None)
-    def profilesListGet(self, clients=True, components=False):
+    def dbus_profilesListGet(self, clients=True, components=False):
         return self._callback("profilesListGet", clients, components)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ss', out_signature='a{ss}',
-                         async_callbacks=None)
-    def progressGet(self, id, profile):
-        return self._callback("progressGet", str(id), str(profile))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{sa{sa{ss}}}',
-                         async_callbacks=None)
-    def progressGetAll(self, profile):
-        return self._callback("progressGetAll", str(profile))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{sa{sa{ss}}}',
-                         async_callbacks=None)
-    def progressGetAllMetadata(self, profile):
-        return self._callback("progressGetAllMetadata", str(profile))
+    def dbus_progressGet(self, id, profile):
+        return self._callback("progressGet", id, profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='',
-                         async_callbacks=('callback', 'errback'))
-    def rosterResync(self, profile_key="@DEFAULT@", callback=None, errback=None):
-        return self._callback("rosterResync", str(profile_key), callback=callback, errback=errback)
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='b',
-                         async_callbacks=None)
-    def saveParamsTemplate(self, filename):
-        return self._callback("saveParamsTemplate", str(filename))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='s', out_signature='a{ss}',
-                         async_callbacks=('callback', 'errback'))
-    def sessionInfosGet(self, profile_key, callback=None, errback=None):
-        return self._callback("sessionInfosGet", str(profile_key), callback=callback, errback=errback)
+    def dbus_progressGetAll(self, profile):
+        return self._callback("progressGetAll", profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sssis', out_signature='',
-                         async_callbacks=None)
-    def setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"):
-        return self._callback("setParam", str(name), str(value), str(category), security_limit, str(profile_key))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssa{ss}s', out_signature='',
-                         async_callbacks=None)
-    def setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"):
-        return self._callback("setPresence", str(to_jid), str(show), statuses, str(profile_key))
-
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='sss', out_signature='',
-                         async_callbacks=None)
-    def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
-        return self._callback("subscription", str(sub_type), str(entity), str(profile_key))
+    def dbus_progressGetAllMetadata(self, profile):
+        return self._callback("progressGetAllMetadata", profile)
 
-    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
-                         in_signature='ssass', out_signature='',
-                         async_callbacks=None)
-    def updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
-        return self._callback("updateContact", str(entity_jid), str(name), groups, str(profile_key))
+    def dbus_rosterResync(self, profile_key="@DEFAULT@"):
+        return self._callback("rosterResync", profile_key)
 
-    def __attributes(self, in_sign):
-        """Return arguments to user given a in_sign
-        @param in_sign: in_sign in the short form (using s,a,i,b etc)
-        @return: list of arguments that correspond to a in_sign (e.g.: "sss" return "arg1, arg2, arg3")"""
-        i = 0
-        idx = 0
-        attr = []
-        while i < len(in_sign):
-            if in_sign[i] not in ["b", "y", "n", "i", "x", "q", "u", "t", "d", "s", "a"]:
-                raise ParseError("Unmanaged attribute type [%c]" % in_sign[i])
-
-            attr.append("arg_%i" % idx)
-            idx += 1
+    def dbus_saveParamsTemplate(self, filename):
+        return self._callback("saveParamsTemplate", filename)
 
-            if in_sign[i] == "a":
-                i += 1
-                if (
-                    in_sign[i] != "{" and in_sign[i] != "("
-                ):  # FIXME: must manage tuples out of arrays
-                    i += 1
-                    continue  # we have a simple type for the array
-                opening_car = in_sign[i]
-                assert opening_car in ["{", "("]
-                closing_car = "}" if opening_car == "{" else ")"
-                opening_count = 1
-                while True:  # we have a dict or a list of tuples
-                    i += 1
-                    if i >= len(in_sign):
-                        raise ParseError("missing }")
-                    if in_sign[i] == opening_car:
-                        opening_count += 1
-                    if in_sign[i] == closing_car:
-                        opening_count -= 1
-                        if opening_count == 0:
-                            break
-            i += 1
-        return attr
+    def dbus_sessionInfosGet(self, profile_key):
+        return self._callback("sessionInfosGet", profile_key)
 
-    def addMethod(self, name, int_suffix, in_sign, out_sign, method, async_=False):
-        """Dynamically add a method to Dbus Bridge"""
-        inspect_args = inspect.getfullargspec(method)
-
-        _arguments = inspect_args.args
-        _defaults = list(inspect_args.defaults or [])
-
-        if inspect.ismethod(method):
-            # if we have a method, we don't want the first argument (usually 'self')
-            del (_arguments[0])
-
-        # first arguments are for the _callback method
-        arguments_callback = ", ".join(
-            [repr(name)]
-            + (
-                (_arguments + ["callback=callback", "errback=errback"])
-                if async_
-                else _arguments
-            )
-        )
-
-        if async_:
-            _arguments.extend(["callback", "errback"])
-            _defaults.extend([None, None])
-
-        # now we create a second list with default values
-        for i in range(1, len(_defaults) + 1):
-            _arguments[-i] = "%s = %s" % (_arguments[-i], repr(_defaults[-i]))
+    def dbus_setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"):
+        return self._callback("setParam", name, value, category, security_limit, profile_key)
 
-        arguments_defaults = ", ".join(_arguments)
+    def dbus_setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"):
+        return self._callback("setPresence", to_jid, show, statuses, profile_key)
 
-        code = compile(
-            "def %(name)s (self,%(arguments_defaults)s): return self._callback(%(arguments_callback)s)"
-            % {
-                "name": name,
-                "arguments_defaults": arguments_defaults,
-                "arguments_callback": arguments_callback,
-            },
-            "<DBus bridge>",
-            "exec",
-        )
-        exec(code)  # FIXME: to the same thing in a cleaner way, without compile/exec
-        method = locals()[name]
-        async_callbacks = ("callback", "errback") if async_ else None
-        setattr(
-            DbusObject,
-            name,
-            dbus.service.method(
-                const_INT_PREFIX + int_suffix,
-                in_signature=in_sign,
-                out_signature=out_sign,
-                async_callbacks=async_callbacks,
-            )(method),
-        )
-        function = getattr(self, name)
-        func_table = self._dbus_class_table[
-            self.__class__.__module__ + "." + self.__class__.__name__
-        ][function._dbus_interface]
-        func_table[function.__name__] = function  # Needed for introspection
+    def dbus_subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
+        return self._callback("subscription", sub_type, entity, profile_key)
 
-    def addSignal(self, name, int_suffix, signature, doc={}):
-        """Dynamically add a signal to Dbus Bridge"""
-        attributes = ", ".join(self.__attributes(signature))
-        # TODO: use doc parameter to name attributes
-
-        # code = compile ('def '+name+' (self,'+attributes+'): log.debug ("'+name+' signal")', '<DBus bridge>','exec') #XXX: the log.debug is too annoying with xmllog
-        code = compile(
-            "def " + name + " (self," + attributes + "): pass", "<DBus bridge>", "exec"
-        )
-        exec(code)
-        signal = locals()[name]
-        setattr(
-            DbusObject,
-            name,
-            dbus.service.signal(const_INT_PREFIX + int_suffix, signature=signature)(
-                signal
-            ),
-        )
-        function = getattr(self, name)
-        func_table = self._dbus_class_table[
-            self.__class__.__module__ + "." + self.__class__.__name__
-        ][function._dbus_interface]
-        func_table[function.__name__] = function  # Needed for introspection
+    def dbus_updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
+        return self._callback("updateContact", entity_jid, name, groups, profile_key)
 
 
-class Bridge(object):
+class Bridge:
+
     def __init__(self):
-        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
         log.info("Init DBus...")
+        self._obj = DBusObject(const_OBJ_PATH)
+
+    async def postInit(self):
         try:
-            self.session_bus = dbus.SessionBus()
-        except dbus.DBusException as e:
-            if e._dbus_error_name == "org.freedesktop.DBus.Error.NotSupported":
+            conn = await client.connect(reactor)
+        except error.DBusException as e:
+            if e.errName == "org.freedesktop.DBus.Error.NotSupported":
                 log.error(
                     _(
-                        "D-Bus is not launched, please see README to see instructions on how to launch it"
+                        "D-Bus is not launched, please see README to see instructions on "
+                        "how to launch it"
                     )
                 )
-            raise BridgeInitError
-        self.dbus_name = dbus.service.BusName(const_INT_PREFIX, self.session_bus)
-        self.dbus_bridge = DbusObject(self.session_bus, const_OBJ_PATH)
+            raise BridgeInitError(str(e))
+
+        conn.exportObject(self._obj)
+        await conn.requestBusName(const_INT_PREFIX)
 
     def _debug(self, action, params, profile):
-        self.dbus_bridge._debug(action, params, profile)
+        self._obj.emitSignal("_debug", action, params, profile)
 
     def actionNew(self, action_data, id, security_limit, profile):
-        self.dbus_bridge.actionNew(action_data, id, security_limit, profile)
+        self._obj.emitSignal("actionNew", action_data, id, security_limit, profile)
 
     def connected(self, jid_s, profile):
-        self.dbus_bridge.connected(jid_s, profile)
+        self._obj.emitSignal("connected", jid_s, profile)
 
     def contactDeleted(self, entity_jid, profile):
-        self.dbus_bridge.contactDeleted(entity_jid, profile)
+        self._obj.emitSignal("contactDeleted", entity_jid, profile)
 
     def disconnected(self, profile):
-        self.dbus_bridge.disconnected(profile)
+        self._obj.emitSignal("disconnected", profile)
 
     def entityDataUpdated(self, jid, name, value, profile):
-        self.dbus_bridge.entityDataUpdated(jid, name, value, profile)
+        self._obj.emitSignal("entityDataUpdated", jid, name, value, profile)
 
     def messageEncryptionStarted(self, to_jid, encryption_data, profile_key):
-        self.dbus_bridge.messageEncryptionStarted(to_jid, encryption_data, profile_key)
+        self._obj.emitSignal("messageEncryptionStarted", to_jid, encryption_data, profile_key)
 
     def messageEncryptionStopped(self, to_jid, encryption_data, profile_key):
-        self.dbus_bridge.messageEncryptionStopped(to_jid, encryption_data, profile_key)
+        self._obj.emitSignal("messageEncryptionStopped", to_jid, encryption_data, profile_key)
 
     def messageNew(self, uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile):
-        self.dbus_bridge.messageNew(uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile)
+        self._obj.emitSignal("messageNew", uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile)
 
     def newContact(self, contact_jid, attributes, groups, profile):
-        self.dbus_bridge.newContact(contact_jid, attributes, groups, profile)
+        self._obj.emitSignal("newContact", contact_jid, attributes, groups, profile)
 
     def paramUpdate(self, name, value, category, profile):
-        self.dbus_bridge.paramUpdate(name, value, category, profile)
+        self._obj.emitSignal("paramUpdate", name, value, category, profile)
 
     def presenceUpdate(self, entity_jid, show, priority, statuses, profile):
-        self.dbus_bridge.presenceUpdate(entity_jid, show, priority, statuses, profile)
+        self._obj.emitSignal("presenceUpdate", entity_jid, show, priority, statuses, profile)
 
     def progressError(self, id, error, profile):
-        self.dbus_bridge.progressError(id, error, profile)
+        self._obj.emitSignal("progressError", id, error, profile)
 
     def progressFinished(self, id, metadata, profile):
-        self.dbus_bridge.progressFinished(id, metadata, profile)
+        self._obj.emitSignal("progressFinished", id, metadata, profile)
 
     def progressStarted(self, id, metadata, profile):
-        self.dbus_bridge.progressStarted(id, metadata, profile)
+        self._obj.emitSignal("progressStarted", id, metadata, profile)
 
     def subscribe(self, sub_type, entity_jid, profile):
-        self.dbus_bridge.subscribe(sub_type, entity_jid, profile)
+        self._obj.emitSignal("subscribe", sub_type, entity_jid, profile)
 
     def register_method(self, name, callback):
-        log.debug("registering DBus bridge method [%s]" % name)
-        self.dbus_bridge.register_method(name, callback)
+        log.debug(f"registering DBus bridge method [{name}]")
+        self._obj.register_method(name, callback)
+
+    def emitSignal(self, name, *args):
+        self._obj.emitSignal(name, *args)
 
-    def addMethod(self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={}):
-        """Dynamically add a method to Dbus Bridge"""
+    def addMethod(
+            self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={}
+    ):
+        """Dynamically add a method to D-Bus Bridge"""
         # FIXME: doc parameter is kept only temporary, the time to remove it from calls
-        log.debug("Adding method [%s] to DBus bridge" % name)
-        self.dbus_bridge.addMethod(name, int_suffix, in_sign, out_sign, method, async_)
+        log.debug(f"Adding method {name!r} to D-Bus bridge")
+        self._obj.plugin_iface.addMethod(
+            Method(name, arguments=in_sign, returns=out_sign)
+        )
+        # we have to create a method here instead of using partialmethod, because txdbus
+        # uses __func__ which doesn't work with partialmethod
+        def caller(self_, *args, **kwargs):
+            return self_._callback(name, *args, **kwargs)
+        setattr(self._obj, f"dbus_{name}", MethodType(caller, self._obj))
         self.register_method(name, method)
 
     def addSignal(self, name, int_suffix, signature, doc={}):
-        self.dbus_bridge.addSignal(name, int_suffix, signature, doc)
-        setattr(Bridge, name, getattr(self.dbus_bridge, name))
\ No newline at end of file
+        """Dynamically add a signal to D-Bus Bridge"""
+        log.debug(f"Adding signal {name!r} to D-Bus bridge")
+        self._obj.plugin_iface.addSignal(Signal(name, signature))
+        setattr(Bridge, name, partialmethod(Bridge.emitSignal, name))
\ No newline at end of file
--- a/sat/core/sat_main.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/sat/core/sat_main.py	Thu Jun 03 15:21:43 2021 +0200
@@ -81,7 +81,7 @@
 
         self.memory = memory.Memory(self)
 
-        # trigger are used to change SàT behaviour
+        # trigger are used to change Libervia behaviour
         self.trigger = (
             trigger.TriggerManager()
         )
@@ -90,14 +90,53 @@
 
         bridge_module = dynamic_import.bridge(bridge_name)
         if bridge_module is None:
-            log.error("Can't find bridge module of name {}".format(bridge_name))
+            log.error(f"Can't find bridge module of name {bridge_name}")
             sys.exit(1)
-        log.info("using {} bridge".format(bridge_name))
+        log.info(f"using {bridge_name} bridge")
         try:
             self.bridge = bridge_module.Bridge()
         except exceptions.BridgeInitError:
-            log.error("Bridge can't be initialised, can't start SàT core")
+            log.error("Bridge can't be initialised, can't start Libervia Backend")
             sys.exit(1)
+
+        defer.ensureDeferred(self._postInit())
+
+    @property
+    def version(self):
+        """Return the short version of Libervia"""
+        return C.APP_VERSION
+
+    @property
+    def full_version(self):
+        """Return the full version of Libervia
+
+        In developement mode, release name and extra data are returned too
+        """
+        version = self.version
+        if version[-1] == "D":
+            # we are in debug version, we add extra data
+            try:
+                return self._version_cache
+            except AttributeError:
+                self._version_cache = "{} « {} » ({})".format(
+                    version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat)
+                )
+                return self._version_cache
+        else:
+            return version
+
+    @property
+    def bridge_name(self):
+        return os.path.splitext(os.path.basename(self.bridge.__file__))[0]
+
+    async def _postInit(self):
+        try:
+            bridge_pi = self.bridge.postInit
+        except AttributeError:
+            pass
+        else:
+            await bridge_pi()
+
         self.bridge.register_method("getReady", lambda: self.initialised)
         self.bridge.register_method("getVersion", lambda: self.full_version)
         self.bridge.register_method("getFeatures", self.getFeatures)
@@ -176,37 +215,8 @@
         self.bridge.register_method("imageResize", self._imageResize)
         self.bridge.register_method("imageGeneratePreview", self._imageGeneratePreview)
         self.bridge.register_method("imageConvert", self._imageConvert)
-        defer.ensureDeferred(self._postInit())
-
-    @property
-    def version(self):
-        """Return the short version of SàT"""
-        return C.APP_VERSION
-
-    @property
-    def full_version(self):
-        """Return the full version of SàT
 
-        In developement mode, release name and extra data are returned too
-        """
-        version = self.version
-        if version[-1] == "D":
-            # we are in debug version, we add extra data
-            try:
-                return self._version_cache
-            except AttributeError:
-                self._version_cache = "{} « {} » ({})".format(
-                    version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat)
-                )
-                return self._version_cache
-        else:
-            return version
 
-    @property
-    def bridge_name(self):
-        return os.path.splitext(os.path.basename(self.bridge.__file__))[0]
-
-    async def _postInit(self):
         await self.memory.initialise()
         self.common_cache = cache.Cache(self, None)
         log.info(_("Memory initialised"))
--- a/sat_frontends/bridge/bridge_frontend.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/sat_frontends/bridge/bridge_frontend.py	Thu Jun 03 15:21:43 2021 +0200
@@ -28,15 +28,14 @@
         @param message (str): error message
         @param condition (str) : error condition
         """
-        Exception.__init__(self)
+        super().__init__()
         self.fullname = str(name)
         self.message = str(message)
         self.condition = str(condition) if condition else ""
         self.module, __, self.classname = str(self.fullname).rpartition(".")
 
     def __str__(self):
-        message = (": %s" % self.message) if self.message else ""
-        return self.classname + message
+        return self.classname + (f": {self.message}" if self.message else "")
 
     def __eq__(self, other):
         return self.classname == other
--- a/setup.py	Thu Jun 03 15:21:43 2021 +0200
+++ b/setup.py	Thu Jun 03 15:21:43 2021 +0200
@@ -54,6 +54,7 @@
     'pyyaml',
     'sqlalchemy >= 1.4',
     'aiosqlite',
+    'txdbus'
 ]
 
 extras_require = {