changeset 2091:f413bfc24458

bridge, quick_frontend: preparation for async bridge bridge can currently have sync and async methods. This commit prepare the transition to fully async bridges: - a new bridgeConnect method must be called to prepare the bridge - quick app, quick profile manager: changed sync calls to async ones - quick app: bridgeConnect can be called automatically or manually depending on connect_bridge parameter of QuickApp
author Goffi <goffi@goffi.org>
date Tue, 13 Dec 2016 22:27:48 +0100
parents 52bd463e6fe7
children 5e79ba00c1e2
files frontends/src/bridge/dbus_bridge.py frontends/src/quick_frontend/quick_app.py frontends/src/quick_frontend/quick_profile_manager.py src/bridge/bridge_constructor/base_constructor.py src/bridge/bridge_constructor/constructors/dbus/dbus_frontend_template.py src/bridge/bridge_constructor/constructors/embedded/embedded_template.py
diffstat 6 files changed, 82 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/bridge/dbus_bridge.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/frontends/src/bridge/dbus_bridge.py	Tue Dec 13 22:27:48 2016 +0100
@@ -60,7 +60,8 @@
 
 
 class Bridge(BridgeFrontend):
-    def __init__(self):
+
+    def bridgeConnect(self, callback, errback):
         try:
             self.sessions_bus = dbus.SessionBus()
             self.db_object = self.sessions_bus.get_object(const_INT_PREFIX,
@@ -72,12 +73,13 @@
         except dbus.exceptions.DBusException, e:
             if e._dbus_error_name in ('org.freedesktop.DBus.Error.ServiceUnknown',
                                       'org.freedesktop.DBus.Error.Spawn.ExecFailed'):
-                raise BridgeExceptionNoService
+                errback(BridgeExceptionNoService())
             elif e._dbus_error_name == 'org.freedesktop.DBus.Error.NotSupported':
                 log.error(_(u"D-Bus is not launched, please see README to see instructions on how to launch it"))
-                raise BridgeInitError
+                errback(BridgeInitError)
             else:
-                raise e
+                errback(e)
+        callback()
         #props = self.db_core_iface.getProperties()
 
     def register_signal(self, functionName, handler, iface="core"):
--- a/frontends/src/quick_frontend/quick_app.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Tue Dec 13 22:27:48 2016 +0100
@@ -54,9 +54,18 @@
 
     def __init__(self, profile):
         self.profile = profile
+        self.connected = False
         self.whoami = None
         self.notifications = {}  # key: bare jid or '' for general, value: notif data
 
+    @property
+    def autodisconnect(self):
+        try:
+            autodisconnect = self._autodisconnect
+        except AttributeError:
+            autodisconnect = False
+        return autodisconnect
+
     def plug(self):
         """Plug the profile to the host"""
         # we get the essential params
@@ -65,12 +74,22 @@
 
     def _plug_profile_jid(self, jid_s):
         self.whoami = jid.JID(jid_s)  # resource might change after the connection
+        self.bridge.isConnected(self.profile, callback=self._plug_profile_isconnected)
+
+    def _plug_profile_isconnected(self, connected):
+        self.connected = connected
+        self.bridge.asyncGetParamA("autodisconnect", "Connection", profile_key=self.profile,
+                                   callback=self._plug_profile_autodisconnect, errback=self._getParamError)
+
+    def _plug_profile_autodisconnect(self, autodisconnect):
+        if C.bool(autodisconnect):
+            self._autodisconnect = True
         self.bridge.asyncGetParamA("autoconnect", "Connection", profile_key=self.profile,
                                    callback=self._plug_profile_autoconnect, errback=self._getParamError)
 
     def _plug_profile_autoconnect(self, value_str):
         autoconnect = C.bool(value_str)
-        if autoconnect and not self.bridge.isConnected(self.profile):
+        if autoconnect and not self.connected:
             self.host.asyncConnect(self.profile, callback=lambda dummy: self._plug_profile_afterconnect())
         else:
             self._plug_profile_afterconnect()
@@ -78,6 +97,7 @@
     def _plug_profile_afterconnect(self):
         # Profile can be connected or not
         # we get cached data
+        self.connected = True
         self.host.bridge.getFeatures(profile_key=self.profile, callback=self._plug_profile_getFeaturesCb, errback=self._plug_profile_getFeaturesEb)
 
     def _plug_profile_getFeaturesEb(self, failure):
@@ -100,7 +120,7 @@
             for key, value in data.iteritems():
                 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile)
 
-        if not self.bridge.isConnected(self.profile):
+        if not self.connected:
             self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=self.profile)
         else:
 
@@ -142,8 +162,8 @@
         # and we launch frontend specific method
         self.host.profilePlugged(self.profile)
 
-    def _getParamError(self, ignore):
-        log.error(_("Can't get profile parameter"))
+    def _getParamError(self, failure):
+        log.error(_("Can't get profile parameter: {msg}").format(msg=failure))
 
 
 class ProfilesManager(object):
@@ -164,6 +184,9 @@
     def __len__(self):
         return len(self._profiles)
 
+    def iteritems(self):
+        return self._profiles.iteritems()
+
     def plug(self, profile):
         if profile in self._profiles:
             raise exceptions.ConflictError('A profile of the name [{}] is already plugged'.format(profile))
@@ -189,10 +212,10 @@
     MB_HANDLER = True  # Set to False if the frontend doesn't manage microblog
     AVATARS_HANDLER = True  # set to False if avatars are not used
 
-    def __init__(self, create_bridge, xmlui, check_options=None):
+    def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True):
         """Create a frontend application
 
-        @param create_bridge: method to use to create the Bridge
+        @param bridge_factory: method to use to create the Bridge
         @param xmlui: xmlui module
         @param check_options: method to call to check options (usually command line arguments)
         """
@@ -220,15 +243,19 @@
         self.trigger = trigger.TriggerManager()  # trigger are used to change the default behaviour
 
         ## bridge ##
-        try:
-            self.bridge = create_bridge()
-        except exceptions.BridgeExceptionNoService:
-            print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
-            sys.exit(1)
-        except exceptions.BridgeInitError:
-            print(_(u"Can't init bridge"))
-            sys.exit(1)
+        self.bridge = bridge_factory()
         ProfileManager.bridge = self.bridge
+        if connect_bridge:
+            self.connectBridge()
+
+        self._notif_id = 0
+        self._notifications = OrderedDict()
+        self.features = None
+
+    def connectBridge(self):
+        self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
+
+    def _bridgeCb(self):
         self.registerSignal("connected")
         self.registerSignal("disconnected")
         self.registerSignal("actionNew")
@@ -255,10 +282,15 @@
         quick_games.Quiz.registerSignals(self)
         quick_games.Radiocol.registerSignals(self)
 
-        self._notif_id = 0
-        self._notifications = OrderedDict()
-        self.media_dir = self.bridge.getConfig('', 'media_dir')
-        self.features = None
+    def _bridgeEb(self, failure):
+        if isinstance(failure, exceptions.BridgeExceptionNoService):
+            print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
+            sys.exit(1)
+        elif isinstance(failure, exceptions.BridgeInitError):
+            print(_(u"Can't init bridge"))
+            sys.exit(1)
+        else:
+            print(_(u"Error while initialising bridge: {}".format(failure)))
 
     @property
     def current_profile(self):
@@ -871,11 +903,10 @@
     def onExit(self):
         """Must be called when the frontend is terminating"""
         to_unplug = []
-        for profile in self.profiles:
-            if self.bridge.isConnected(profile):
-                if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)):
-                    #The user wants autodisconnection
-                    self.disconnect(profile)
+        for profile, profile_manager in self.profiles.iteritems():
+            if profile_manager.connected(profile) and profile_manager.autodisconnect:
+                #The user wants autodisconnection
+                self.disconnect(profile)
             to_unplug.append(profile)
         for profile in to_unplug:
             self.unplug_profile(profile)
--- a/frontends/src/quick_frontend/quick_profile_manager.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/frontends/src/quick_frontend/quick_profile_manager.py	Tue Dec 13 22:27:48 2016 +0100
@@ -108,15 +108,22 @@
                 self._autoconnect=False
             self.host.actionManager(data, callback=authenticate_cb, profile=profile)
 
-        for profile_key in profile_keys:
-            profile = self.host.bridge.getProfileName(profile_key)
+        def getProfileNameCb(profile):
             if not profile:
+                # FIXME: this method is not handling manual mode correclty anymore
+                #        must be thought to be handled asynchronously
                 self._autoconnect = False # manual mode
                 msg = _("Trying to plug an unknown profile key ({})".format(profile_key))
                 log.warning(msg)
-                self.showDialog(_("Profile plugging in error"), msg, 'error')
-                break
-            self.host.launchAction(C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=profile)
+                self.host.showDialog(_("Profile plugging in error"), msg, 'error')
+            else:
+                self.host.launchAction(C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=profile)
+
+        def getProfileNameEb(failure):
+            log.error(u"Can't retrieve profile name: {}".format(failure))
+
+        for profile_key in profile_keys:
+            self.host.bridge.getProfileName(profile_key, callback=getProfileNameCb, errback=getProfileNameEb)
 
 
     def getParamError(self, dummy):
--- a/src/bridge/bridge_constructor/base_constructor.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/src/bridge/bridge_constructor/base_constructor.py	Tue Dec 13 22:27:48 2016 +0100
@@ -176,7 +176,7 @@
         @param name: dictionary of arguments name like given by getArgumentsDoc
         @param default: dictionary of default values, like given by getDefault
         @param unicode_protect: activate unicode protection on strings (return strings as unicode(str))
-        @return: list of arguments that correspond to a signature (e.g.: "sss" return "arg1, arg2, arg3")
+        @return (str): arguments that correspond to a signature (e.g.: "sss" return "arg1, arg2, arg3")
         """
         idx = 0
         attr_string = []
--- a/src/bridge/bridge_constructor/constructors/dbus/dbus_frontend_template.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/src/bridge/bridge_constructor/constructors/dbus/dbus_frontend_template.py	Tue Dec 13 22:27:48 2016 +0100
@@ -60,7 +60,8 @@
 
 
 class Bridge(BridgeFrontend):
-    def __init__(self):
+
+    def bridgeConnect(self, callback, errback):
         try:
             self.sessions_bus = dbus.SessionBus()
             self.db_object = self.sessions_bus.get_object(const_INT_PREFIX,
@@ -72,12 +73,13 @@
         except dbus.exceptions.DBusException, e:
             if e._dbus_error_name in ('org.freedesktop.DBus.Error.ServiceUnknown',
                                       'org.freedesktop.DBus.Error.Spawn.ExecFailed'):
-                raise BridgeExceptionNoService
+                errback(BridgeExceptionNoService())
             elif e._dbus_error_name == 'org.freedesktop.DBus.Error.NotSupported':
                 log.error(_(u"D-Bus is not launched, please see README to see instructions on how to launch it"))
-                raise BridgeInitError
+                errback(BridgeInitError)
             else:
-                raise e
+                errback(e)
+        callback()
         #props = self.db_core_iface.getProperties()
 
     def register_signal(self, functionName, handler, iface="core"):
--- a/src/bridge/bridge_constructor/constructors/embedded/embedded_template.py	Sun Dec 04 21:35:23 2016 +0100
+++ b/src/bridge/bridge_constructor/constructors/embedded/embedded_template.py	Tue Dec 13 22:27:48 2016 +0100
@@ -31,6 +31,9 @@
             "plugin": {}
             }
 
+    def bridgeConnect(self, callback, errback):
+        callback()
+
     def register_method(self, name, callback):
         log.debug(u"registering embedded bridge method [{}]".format(name))
         if name in self._methods_cbs: