comparison frontends/src/quick_frontend/quick_app.py @ 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 0931b5a6213c
children 4bc408b549cd
comparison
equal deleted inserted replaced
2090:52bd463e6fe7 2091:f413bfc24458
52 bridge = None 52 bridge = None
53 cache_keys_to_get = ['avatar'] 53 cache_keys_to_get = ['avatar']
54 54
55 def __init__(self, profile): 55 def __init__(self, profile):
56 self.profile = profile 56 self.profile = profile
57 self.connected = False
57 self.whoami = None 58 self.whoami = None
58 self.notifications = {} # key: bare jid or '' for general, value: notif data 59 self.notifications = {} # key: bare jid or '' for general, value: notif data
60
61 @property
62 def autodisconnect(self):
63 try:
64 autodisconnect = self._autodisconnect
65 except AttributeError:
66 autodisconnect = False
67 return autodisconnect
59 68
60 def plug(self): 69 def plug(self):
61 """Plug the profile to the host""" 70 """Plug the profile to the host"""
62 # we get the essential params 71 # we get the essential params
63 self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile, 72 self.bridge.asyncGetParamA("JabberID", "Connection", profile_key=self.profile,
64 callback=self._plug_profile_jid, errback=self._getParamError) 73 callback=self._plug_profile_jid, errback=self._getParamError)
65 74
66 def _plug_profile_jid(self, jid_s): 75 def _plug_profile_jid(self, jid_s):
67 self.whoami = jid.JID(jid_s) # resource might change after the connection 76 self.whoami = jid.JID(jid_s) # resource might change after the connection
77 self.bridge.isConnected(self.profile, callback=self._plug_profile_isconnected)
78
79 def _plug_profile_isconnected(self, connected):
80 self.connected = connected
81 self.bridge.asyncGetParamA("autodisconnect", "Connection", profile_key=self.profile,
82 callback=self._plug_profile_autodisconnect, errback=self._getParamError)
83
84 def _plug_profile_autodisconnect(self, autodisconnect):
85 if C.bool(autodisconnect):
86 self._autodisconnect = True
68 self.bridge.asyncGetParamA("autoconnect", "Connection", profile_key=self.profile, 87 self.bridge.asyncGetParamA("autoconnect", "Connection", profile_key=self.profile,
69 callback=self._plug_profile_autoconnect, errback=self._getParamError) 88 callback=self._plug_profile_autoconnect, errback=self._getParamError)
70 89
71 def _plug_profile_autoconnect(self, value_str): 90 def _plug_profile_autoconnect(self, value_str):
72 autoconnect = C.bool(value_str) 91 autoconnect = C.bool(value_str)
73 if autoconnect and not self.bridge.isConnected(self.profile): 92 if autoconnect and not self.connected:
74 self.host.asyncConnect(self.profile, callback=lambda dummy: self._plug_profile_afterconnect()) 93 self.host.asyncConnect(self.profile, callback=lambda dummy: self._plug_profile_afterconnect())
75 else: 94 else:
76 self._plug_profile_afterconnect() 95 self._plug_profile_afterconnect()
77 96
78 def _plug_profile_afterconnect(self): 97 def _plug_profile_afterconnect(self):
79 # Profile can be connected or not 98 # Profile can be connected or not
80 # we get cached data 99 # we get cached data
100 self.connected = True
81 self.host.bridge.getFeatures(profile_key=self.profile, callback=self._plug_profile_getFeaturesCb, errback=self._plug_profile_getFeaturesEb) 101 self.host.bridge.getFeatures(profile_key=self.profile, callback=self._plug_profile_getFeaturesCb, errback=self._plug_profile_getFeaturesEb)
82 102
83 def _plug_profile_getFeaturesEb(self, failure): 103 def _plug_profile_getFeaturesEb(self, failure):
84 log.error(u"Couldn't get features: {}".format(failure)) 104 log.error(u"Couldn't get features: {}".format(failure))
85 self._plug_profile_getFeaturesCb({}) 105 self._plug_profile_getFeaturesCb({})
98 118
99 for entity_s, data in cached_values.iteritems(): 119 for entity_s, data in cached_values.iteritems():
100 for key, value in data.iteritems(): 120 for key, value in data.iteritems():
101 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile) 121 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile)
102 122
103 if not self.bridge.isConnected(self.profile): 123 if not self.connected:
104 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=self.profile) 124 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, '', profile=self.profile)
105 else: 125 else:
106 126
107 contact_list.fill() 127 contact_list.fill()
108 self.host.setPresenceStatus(profile=self.profile) 128 self.host.setPresenceStatus(profile=self.profile)
140 160
141 # At this point, profile should be fully plugged 161 # At this point, profile should be fully plugged
142 # and we launch frontend specific method 162 # and we launch frontend specific method
143 self.host.profilePlugged(self.profile) 163 self.host.profilePlugged(self.profile)
144 164
145 def _getParamError(self, ignore): 165 def _getParamError(self, failure):
146 log.error(_("Can't get profile parameter")) 166 log.error(_("Can't get profile parameter: {msg}").format(msg=failure))
147 167
148 168
149 class ProfilesManager(object): 169 class ProfilesManager(object):
150 """Class managing collection of profiles""" 170 """Class managing collection of profiles"""
151 171
161 def __getitem__(self, profile): 181 def __getitem__(self, profile):
162 return self._profiles[profile] 182 return self._profiles[profile]
163 183
164 def __len__(self): 184 def __len__(self):
165 return len(self._profiles) 185 return len(self._profiles)
186
187 def iteritems(self):
188 return self._profiles.iteritems()
166 189
167 def plug(self, profile): 190 def plug(self, profile):
168 if profile in self._profiles: 191 if profile in self._profiles:
169 raise exceptions.ConflictError('A profile of the name [{}] is already plugged'.format(profile)) 192 raise exceptions.ConflictError('A profile of the name [{}] is already plugged'.format(profile))
170 self._profiles[profile] = ProfileManager(profile) 193 self._profiles[profile] = ProfileManager(profile)
187 class QuickApp(object): 210 class QuickApp(object):
188 """This class contain the main methods needed for the frontend""" 211 """This class contain the main methods needed for the frontend"""
189 MB_HANDLER = True # Set to False if the frontend doesn't manage microblog 212 MB_HANDLER = True # Set to False if the frontend doesn't manage microblog
190 AVATARS_HANDLER = True # set to False if avatars are not used 213 AVATARS_HANDLER = True # set to False if avatars are not used
191 214
192 def __init__(self, create_bridge, xmlui, check_options=None): 215 def __init__(self, bridge_factory, xmlui, check_options=None, connect_bridge=True):
193 """Create a frontend application 216 """Create a frontend application
194 217
195 @param create_bridge: method to use to create the Bridge 218 @param bridge_factory: method to use to create the Bridge
196 @param xmlui: xmlui module 219 @param xmlui: xmlui module
197 @param check_options: method to call to check options (usually command line arguments) 220 @param check_options: method to call to check options (usually command line arguments)
198 """ 221 """
199 self.xmlui = xmlui 222 self.xmlui = xmlui
200 self.menus = quick_menus.QuickMenusManager(self) 223 self.menus = quick_menus.QuickMenusManager(self)
218 241
219 # triggers 242 # triggers
220 self.trigger = trigger.TriggerManager() # trigger are used to change the default behaviour 243 self.trigger = trigger.TriggerManager() # trigger are used to change the default behaviour
221 244
222 ## bridge ## 245 ## bridge ##
223 try: 246 self.bridge = bridge_factory()
224 self.bridge = create_bridge()
225 except exceptions.BridgeExceptionNoService:
226 print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
227 sys.exit(1)
228 except exceptions.BridgeInitError:
229 print(_(u"Can't init bridge"))
230 sys.exit(1)
231 ProfileManager.bridge = self.bridge 247 ProfileManager.bridge = self.bridge
248 if connect_bridge:
249 self.connectBridge()
250
251 self._notif_id = 0
252 self._notifications = OrderedDict()
253 self.features = None
254
255 def connectBridge(self):
256 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
257
258 def _bridgeCb(self):
232 self.registerSignal("connected") 259 self.registerSignal("connected")
233 self.registerSignal("disconnected") 260 self.registerSignal("disconnected")
234 self.registerSignal("actionNew") 261 self.registerSignal("actionNew")
235 self.registerSignal("newContact") 262 self.registerSignal("newContact")
236 self.registerSignal("messageNew") 263 self.registerSignal("messageNew")
253 # FIXME: do it dynamically 280 # FIXME: do it dynamically
254 quick_games.Tarot.registerSignals(self) 281 quick_games.Tarot.registerSignals(self)
255 quick_games.Quiz.registerSignals(self) 282 quick_games.Quiz.registerSignals(self)
256 quick_games.Radiocol.registerSignals(self) 283 quick_games.Radiocol.registerSignals(self)
257 284
258 self._notif_id = 0 285 def _bridgeEb(self, failure):
259 self._notifications = OrderedDict() 286 if isinstance(failure, exceptions.BridgeExceptionNoService):
260 self.media_dir = self.bridge.getConfig('', 'media_dir') 287 print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
261 self.features = None 288 sys.exit(1)
289 elif isinstance(failure, exceptions.BridgeInitError):
290 print(_(u"Can't init bridge"))
291 sys.exit(1)
292 else:
293 print(_(u"Error while initialising bridge: {}".format(failure)))
262 294
263 @property 295 @property
264 def current_profile(self): 296 def current_profile(self):
265 """Profile that a user would expect to use""" 297 """Profile that a user would expect to use"""
266 try: 298 try:
869 self.bridge.disconnect(profile) 901 self.bridge.disconnect(profile)
870 902
871 def onExit(self): 903 def onExit(self):
872 """Must be called when the frontend is terminating""" 904 """Must be called when the frontend is terminating"""
873 to_unplug = [] 905 to_unplug = []
874 for profile in self.profiles: 906 for profile, profile_manager in self.profiles.iteritems():
875 if self.bridge.isConnected(profile): 907 if profile_manager.connected(profile) and profile_manager.autodisconnect:
876 if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)): 908 #The user wants autodisconnection
877 #The user wants autodisconnection 909 self.disconnect(profile)
878 self.disconnect(profile)
879 to_unplug.append(profile) 910 to_unplug.append(profile)
880 for profile in to_unplug: 911 for profile in to_unplug:
881 self.unplug_profile(profile) 912 self.unplug_profile(profile)