Mercurial > libervia-backend
comparison sat_frontends/quick_frontend/quick_app.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | e7cbe662838b |
children | f8e3789912d0 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
38 from collections import OrderedDict | 38 from collections import OrderedDict |
39 import time | 39 import time |
40 | 40 |
41 try: | 41 try: |
42 # FIXME: to be removed when an acceptable solution is here | 42 # FIXME: to be removed when an acceptable solution is here |
43 unicode("") # XXX: unicode doesn't exist in pyjamas | 43 str("") # XXX: unicode doesn't exist in pyjamas |
44 except ( | 44 except ( |
45 TypeError, | 45 TypeError, |
46 AttributeError, | 46 AttributeError, |
47 ): # Error raised is not the same depending on pyjsbuild options | 47 ): # Error raised is not the same depending on pyjsbuild options |
48 unicode = str | 48 str = str |
49 | 49 |
50 | 50 |
51 class ProfileManager(object): | 51 class ProfileManager(object): |
52 """Class managing all data relative to one profile, and plugging in mechanism""" | 52 """Class managing all data relative to one profile, and plugging in mechanism""" |
53 | 53 |
137 callback=self._plug_profile_getFeaturesCb, | 137 callback=self._plug_profile_getFeaturesCb, |
138 errback=self._plug_profile_getFeaturesEb, | 138 errback=self._plug_profile_getFeaturesEb, |
139 ) | 139 ) |
140 | 140 |
141 def _plug_profile_getFeaturesEb(self, failure): | 141 def _plug_profile_getFeaturesEb(self, failure): |
142 log.error(u"Couldn't get features: {}".format(failure)) | 142 log.error("Couldn't get features: {}".format(failure)) |
143 self._plug_profile_getFeaturesCb({}) | 143 self._plug_profile_getFeaturesCb({}) |
144 | 144 |
145 def _plug_profile_getFeaturesCb(self, features): | 145 def _plug_profile_getFeaturesCb(self, features): |
146 self.host.features = features | 146 self.host.features = features |
147 # FIXME: we don't use cached value at the moment, but keep the code for later use | 147 # FIXME: we don't use cached value at the moment, but keep the code for later use |
152 # callback=self._plug_profile_gotCachedValues, | 152 # callback=self._plug_profile_gotCachedValues, |
153 # errback=self._plug_profile_failedCachedValues) | 153 # errback=self._plug_profile_failedCachedValues) |
154 self._plug_profile_gotCachedValues({}) | 154 self._plug_profile_gotCachedValues({}) |
155 | 155 |
156 def _plug_profile_failedCachedValues(self, failure): | 156 def _plug_profile_failedCachedValues(self, failure): |
157 log.error(u"Couldn't get cached values: {}".format(failure)) | 157 log.error("Couldn't get cached values: {}".format(failure)) |
158 self._plug_profile_gotCachedValues({}) | 158 self._plug_profile_gotCachedValues({}) |
159 | 159 |
160 def _plug_profile_gotCachedValues(self, cached_values): | 160 def _plug_profile_gotCachedValues(self, cached_values): |
161 contact_list = self.host.contact_lists[self.profile] | 161 contact_list = self.host.contact_lists[self.profile] |
162 # add the contact list and its listener | 162 # add the contact list and its listener |
163 for entity_s, data in cached_values.iteritems(): | 163 for entity_s, data in cached_values.items(): |
164 for key, value in data.iteritems(): | 164 for key, value in data.items(): |
165 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile) | 165 self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile) |
166 | 166 |
167 if not self.connected: | 167 if not self.connected: |
168 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=self.profile) | 168 self.host.setPresenceStatus(C.PRESENCE_UNAVAILABLE, "", profile=self.profile) |
169 else: | 169 else: |
201 contact, key, data[key], self.profile | 201 contact, key, data[key], self.profile |
202 ) | 202 ) |
203 | 203 |
204 for contact in presences: | 204 for contact in presences: |
205 for res in presences[contact]: | 205 for res in presences[contact]: |
206 jabber_id = (u"%s/%s" % (jid.JID(contact).bare, res)) if res else contact | 206 jabber_id = ("%s/%s" % (jid.JID(contact).bare, res)) if res else contact |
207 show = presences[contact][res][0] | 207 show = presences[contact][res][0] |
208 priority = presences[contact][res][1] | 208 priority = presences[contact][res][1] |
209 statuses = presences[contact][res][2] | 209 statuses = presences[contact][res][2] |
210 self.host.presenceUpdateHandler( | 210 self.host.presenceUpdateHandler( |
211 jabber_id, show, priority, statuses, self.profile | 211 jabber_id, show, priority, statuses, self.profile |
214 contact, | 214 contact, |
215 ["avatar", "nick"], | 215 ["avatar", "nick"], |
216 self.profile, | 216 self.profile, |
217 callback=lambda data, contact=contact: gotEntityData(data, contact), | 217 callback=lambda data, contact=contact: gotEntityData(data, contact), |
218 errback=lambda failure, contact=contact: log.debug( | 218 errback=lambda failure, contact=contact: log.debug( |
219 u"No cache data for {}".format(contact) | 219 "No cache data for {}".format(contact) |
220 ), | 220 ), |
221 ) | 221 ) |
222 | 222 |
223 # At this point, profile should be fully plugged | 223 # At this point, profile should be fully plugged |
224 # and we launch frontend specific method | 224 # and we launch frontend specific method |
236 | 236 |
237 def __contains__(self, profile): | 237 def __contains__(self, profile): |
238 return profile in self._profiles | 238 return profile in self._profiles |
239 | 239 |
240 def __iter__(self): | 240 def __iter__(self): |
241 return self._profiles.iterkeys() | 241 return iter(self._profiles.keys()) |
242 | 242 |
243 def __getitem__(self, profile): | 243 def __getitem__(self, profile): |
244 return self._profiles[profile] | 244 return self._profiles[profile] |
245 | 245 |
246 def __len__(self): | 246 def __len__(self): |
247 return len(self._profiles) | 247 return len(self._profiles) |
248 | 248 |
249 def iteritems(self): | 249 def items(self): |
250 return self._profiles.iteritems() | 250 return self._profiles.items() |
251 | 251 |
252 def itervalues(self): | 252 def values(self): |
253 return self._profiles.itervalues() | 253 return self._profiles.values() |
254 | 254 |
255 def plug(self, profile): | 255 def plug(self, profile): |
256 if profile in self._profiles: | 256 if profile in self._profiles: |
257 raise exceptions.ConflictError( | 257 raise exceptions.ConflictError( |
258 "A profile of the name [{}] is already plugged".format(profile) | 258 "A profile of the name [{}] is already plugged".format(profile) |
269 host.contact_lists[profile].unplug() | 269 host.contact_lists[profile].unplug() |
270 | 270 |
271 del self._profiles[profile] | 271 del self._profiles[profile] |
272 | 272 |
273 def chooseOneProfile(self): | 273 def chooseOneProfile(self): |
274 return self._profiles.keys()[0] | 274 return list(self._profiles.keys())[0] |
275 | 275 |
276 | 276 |
277 class QuickApp(object): | 277 class QuickApp(object): |
278 """This class contain the main methods needed for the frontend""" | 278 """This class contain the main methods needed for the frontend""" |
279 | 279 |
348 | 348 |
349 def _namespacesGetCb(self, ns_map): | 349 def _namespacesGetCb(self, ns_map): |
350 self.ns_map = ns_map | 350 self.ns_map = ns_map |
351 | 351 |
352 def _namespacesGetEb(self, failure_): | 352 def _namespacesGetEb(self, failure_): |
353 log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_)) | 353 log.error(_("Can't get namespaces map: {msg}").format(msg=failure_)) |
354 | 354 |
355 def _encryptionPluginsGetCb(self, plugins): | 355 def _encryptionPluginsGetCb(self, plugins): |
356 self.encryption_plugins = plugins | 356 self.encryption_plugins = plugins |
357 | 357 |
358 def _encryptionPluginsGetEb(self, failure_): | 358 def _encryptionPluginsGetEb(self, failure_): |
359 log.warning(_(u"Can't retrieve encryption plugins: {msg}").format(msg=failure_)) | 359 log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_)) |
360 | 360 |
361 def onBridgeConnected(self): | 361 def onBridgeConnected(self): |
362 self.bridge.namespacesGet( | 362 self.bridge.namespacesGet( |
363 callback=self._namespacesGetCb, errback=self._namespacesGetEb) | 363 callback=self._namespacesGetCb, errback=self._namespacesGetEb) |
364 # we cache available encryption plugins, as we'll use them on earch | 364 # we cache available encryption plugins, as we'll use them on earch |
400 quick_games.Radiocol.registerSignals(self) | 400 quick_games.Radiocol.registerSignals(self) |
401 self.onBridgeConnected() | 401 self.onBridgeConnected() |
402 | 402 |
403 def _bridgeEb(self, failure): | 403 def _bridgeEb(self, failure): |
404 if isinstance(failure, exceptions.BridgeExceptionNoService): | 404 if isinstance(failure, exceptions.BridgeExceptionNoService): |
405 print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) | 405 print((_("Can't connect to SàT backend, are you sure it's launched ?"))) |
406 sys.exit(1) | 406 sys.exit(1) |
407 elif isinstance(failure, exceptions.BridgeInitError): | 407 elif isinstance(failure, exceptions.BridgeInitError): |
408 print(_(u"Can't init bridge")) | 408 print((_("Can't init bridge"))) |
409 sys.exit(1) | 409 sys.exit(1) |
410 else: | 410 else: |
411 print(_(u"Error while initialising bridge: {}".format(failure))) | 411 print((_("Error while initialising bridge: {}".format(failure)))) |
412 | 412 |
413 @property | 413 @property |
414 def current_profile(self): | 414 def current_profile(self): |
415 """Profile that a user would expect to use""" | 415 """Profile that a user would expect to use""" |
416 try: | 416 try: |
457 @param state(bool): True: if the backend is resynchronising | 457 @param state(bool): True: if the backend is resynchronising |
458 False when we lose synchronisation, for instance if frontend is going to sleep | 458 False when we lose synchronisation, for instance if frontend is going to sleep |
459 or if connection has been lost and a reconnection is needed | 459 or if connection has been lost and a reconnection is needed |
460 """ | 460 """ |
461 if state: | 461 if state: |
462 log.debug(u"we are synchronised with server") | 462 log.debug("we are synchronised with server") |
463 if self.AUTO_RESYNC: | 463 if self.AUTO_RESYNC: |
464 # we are resynchronising all widgets | 464 # we are resynchronising all widgets |
465 log.debug(u"doing a full widgets resynchronisation") | 465 log.debug("doing a full widgets resynchronisation") |
466 for w in self.widgets: | 466 for w in self.widgets: |
467 try: | 467 try: |
468 resync = w.resync | 468 resync = w.resync |
469 except AttributeError: | 469 except AttributeError: |
470 pass | 470 pass |
472 resync() | 472 resync() |
473 self.contact_lists.fill() | 473 self.contact_lists.fill() |
474 | 474 |
475 self._sync = state | 475 self._sync = state |
476 else: | 476 else: |
477 log.debug(u"we have lost synchronisation with server") | 477 log.debug("we have lost synchronisation with server") |
478 self._sync = state | 478 self._sync = state |
479 # we've lost synchronisation, all widgets must be notified | 479 # we've lost synchronisation, all widgets must be notified |
480 # note: this is always called independently of AUTO_RESYNC | 480 # note: this is always called independently of AUTO_RESYNC |
481 for w in self.widgets: | 481 for w in self.widgets: |
482 try: | 482 try: |
494 None for calling an automatically named handler (function_name + 'Handler') | 494 None for calling an automatically named handler (function_name + 'Handler') |
495 @param iface (str): interface of the bridge to use ('core' or 'plugin') | 495 @param iface (str): interface of the bridge to use ('core' or 'plugin') |
496 @param with_profile (boolean): True if the signal concerns a specific profile, | 496 @param with_profile (boolean): True if the signal concerns a specific profile, |
497 in that case the profile name has to be passed by the caller | 497 in that case the profile name has to be passed by the caller |
498 """ | 498 """ |
499 log.debug(u"registering signal {name}".format(name=function_name)) | 499 log.debug("registering signal {name}".format(name=function_name)) |
500 if handler is None: | 500 if handler is None: |
501 handler = getattr(self, "{}{}".format(function_name, "Handler")) | 501 handler = getattr(self, "{}{}".format(function_name, "Handler")) |
502 if not with_profile: | 502 if not with_profile: |
503 self.bridge.register_signal(function_name, handler, iface) | 503 self.bridge.register_signal(function_name, handler, iface) |
504 return | 504 return |
580 listeners = self._listeners[type_] | 580 listeners = self._listeners[type_] |
581 except KeyError: | 581 except KeyError: |
582 pass | 582 pass |
583 else: | 583 else: |
584 profile = kwargs.get("profile") | 584 profile = kwargs.get("profile") |
585 for listener, profiles_filter in listeners.iteritems(): | 585 for listener, profiles_filter in listeners.items(): |
586 if profile is None or not profiles_filter or profile in profiles_filter: | 586 if profile is None or not profiles_filter or profile in profiles_filter: |
587 listener(*args, **kwargs) | 587 listener(*args, **kwargs) |
588 | 588 |
589 def check_profile(self, profile): | 589 def check_profile(self, profile): |
590 """Tell if the profile is currently followed by the application, and ready""" | 590 """Tell if the profile is currently followed by the application, and ready""" |
615 | 615 |
616 # profile is ready, we can call send signals that where is cache | 616 # profile is ready, we can call send signals that where is cache |
617 cached_signals = self.signals_cache.pop(profile, []) | 617 cached_signals = self.signals_cache.pop(profile, []) |
618 for function_name, handler, args, kwargs in cached_signals: | 618 for function_name, handler, args, kwargs in cached_signals: |
619 log.debug( | 619 log.debug( |
620 u"Calling cached signal [%s] with args %s and kwargs %s" | 620 "Calling cached signal [%s] with args %s and kwargs %s" |
621 % (function_name, args, kwargs) | 621 % (function_name, args, kwargs) |
622 ) | 622 ) |
623 handler(*args, **kwargs) | 623 handler(*args, **kwargs) |
624 | 624 |
625 self.callListeners("profilePlugged", profile=profile) | 625 self.callListeners("profilePlugged", profile=profile) |
630 if not callback: | 630 if not callback: |
631 callback = lambda __: None | 631 callback = lambda __: None |
632 if not errback: | 632 if not errback: |
633 | 633 |
634 def errback(failure): | 634 def errback(failure): |
635 log.error(_(u"Can't connect profile [%s]") % failure) | 635 log.error(_("Can't connect profile [%s]") % failure) |
636 try: | 636 try: |
637 module = failure.module | 637 module = failure.module |
638 except AttributeError: | 638 except AttributeError: |
639 module = "" | 639 module = "" |
640 try: | 640 try: |
788 def messageStateHandler(self, uid, status, profile): | 788 def messageStateHandler(self, uid, status, profile): |
789 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): | 789 for widget in self.widgets.getWidgets(quick_chat.QuickChat, profiles=(profile,)): |
790 widget.onMessageState(uid, status, profile) | 790 widget.onMessageState(uid, status, profile) |
791 | 791 |
792 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): | 792 def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE): |
793 if not subject and not extra and (not message or message == {u'': u''}): | 793 if not subject and not extra and (not message or message == {'': ''}): |
794 log.debug(u"Not sending empty message") | 794 log.debug("Not sending empty message") |
795 return | 795 return |
796 | 796 |
797 if subject is None: | 797 if subject is None: |
798 subject = {} | 798 subject = {} |
799 if extra is None: | 799 if extra is None: |
810 | 810 |
811 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key): | 811 if not self.trigger.point("messageSendTrigger", to_jid, message, subject, mess_type, extra, callback, errback, profile_key=profile_key): |
812 return | 812 return |
813 | 813 |
814 self.bridge.messageSend( | 814 self.bridge.messageSend( |
815 unicode(to_jid), | 815 str(to_jid), |
816 message, | 816 message, |
817 subject, | 817 subject, |
818 mess_type, | 818 mess_type, |
819 extra, | 819 extra, |
820 profile_key, | 820 profile_key, |
855 self.callListeners("presence", entity, show, priority, statuses, profile=profile) | 855 self.callListeners("presence", entity, show, priority, statuses, profile=profile) |
856 | 856 |
857 def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile): | 857 def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile): |
858 """Called when a MUC room is joined""" | 858 """Called when a MUC room is joined""" |
859 log.debug( | 859 log.debug( |
860 u"Room [{room_jid}] joined by {profile}, users presents:{users}".format( | 860 "Room [{room_jid}] joined by {profile}, users presents:{users}".format( |
861 room_jid=room_jid_s, profile=profile, users=occupants.keys() | 861 room_jid=room_jid_s, profile=profile, users=list(occupants.keys()) |
862 ) | 862 ) |
863 ) | 863 ) |
864 room_jid = jid.JID(room_jid_s) | 864 room_jid = jid.JID(room_jid_s) |
865 self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) | 865 self.contact_lists[profile].setSpecial(room_jid, C.CONTACT_SPECIAL_GROUP) |
866 self.widgets.getOrCreateWidget( | 866 self.widgets.getOrCreateWidget( |
874 ) | 874 ) |
875 | 875 |
876 def mucRoomLeftHandler(self, room_jid_s, profile): | 876 def mucRoomLeftHandler(self, room_jid_s, profile): |
877 """Called when a MUC room is left""" | 877 """Called when a MUC room is left""" |
878 log.debug( | 878 log.debug( |
879 u"Room [%(room_jid)s] left by %(profile)s" | 879 "Room [%(room_jid)s] left by %(profile)s" |
880 % {"room_jid": room_jid_s, "profile": profile} | 880 % {"room_jid": room_jid_s, "profile": profile} |
881 ) | 881 ) |
882 room_jid = jid.JID(room_jid_s) | 882 room_jid = jid.JID(room_jid_s) |
883 chat_widget = self.widgets.getWidget(quick_chat.QuickChat, room_jid, profile) | 883 chat_widget = self.widgets.getWidget(quick_chat.QuickChat, room_jid, profile) |
884 if chat_widget: | 884 if chat_widget: |
891 chat_widget = self.widgets.getOrCreateWidget( | 891 chat_widget = self.widgets.getOrCreateWidget( |
892 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile | 892 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile |
893 ) | 893 ) |
894 chat_widget.changeUserNick(old_nick, new_nick) | 894 chat_widget.changeUserNick(old_nick, new_nick) |
895 log.debug( | 895 log.debug( |
896 u"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" | 896 "user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]" |
897 % {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid} | 897 % {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid} |
898 ) | 898 ) |
899 | 899 |
900 def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile): | 900 def mucRoomNewSubjectHandler(self, room_jid_s, subject, profile): |
901 """Called when subject of MUC room change""" | 901 """Called when subject of MUC room change""" |
903 chat_widget = self.widgets.getOrCreateWidget( | 903 chat_widget = self.widgets.getOrCreateWidget( |
904 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile | 904 quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile |
905 ) | 905 ) |
906 chat_widget.setSubject(subject) | 906 chat_widget.setSubject(subject) |
907 log.debug( | 907 log.debug( |
908 u"new subject for room [%(room_jid)s]: %(subject)s" | 908 "new subject for room [%(room_jid)s]: %(subject)s" |
909 % {"room_jid": room_jid, "subject": subject} | 909 % {"room_jid": room_jid, "subject": subject} |
910 ) | 910 ) |
911 | 911 |
912 def chatStateReceivedHandler(self, from_jid_s, state, profile): | 912 def chatStateReceivedHandler(self, from_jid_s, state, profile): |
913 """Called when a new chat state (XEP-0085) is received. | 913 """Called when a new chat state (XEP-0085) is received. |
972 @return (iter[dict]): notifications | 972 @return (iter[dict]): notifications |
973 """ | 973 """ |
974 main_notif_dict = self.profiles[profile].notifications | 974 main_notif_dict = self.profiles[profile].notifications |
975 | 975 |
976 if entity is C.ENTITY_ALL: | 976 if entity is C.ENTITY_ALL: |
977 selected_notifs = main_notif_dict.itervalues() | 977 selected_notifs = iter(main_notif_dict.values()) |
978 exact_jid = False | 978 exact_jid = False |
979 else: | 979 else: |
980 if entity is None: | 980 if entity is None: |
981 key = "" | 981 key = "" |
982 exact_jid = False | 982 exact_jid = False |
987 selected_notifs = (main_notif_dict.setdefault(key, {}),) | 987 selected_notifs = (main_notif_dict.setdefault(key, {}),) |
988 | 988 |
989 for notifs_from_select in selected_notifs: | 989 for notifs_from_select in selected_notifs: |
990 | 990 |
991 if type_ is None: | 991 if type_ is None: |
992 type_notifs = notifs_from_select.itervalues() | 992 type_notifs = iter(notifs_from_select.values()) |
993 else: | 993 else: |
994 type_notifs = (notifs_from_select.get(type_, []),) | 994 type_notifs = (notifs_from_select.get(type_, []),) |
995 | 995 |
996 for notifs in type_notifs: | 996 for notifs in type_notifs: |
997 for notif in notifs: | 997 for notif in notifs: |
1078 """ | 1078 """ |
1079 callbacks = self._progress_ids.setdefault(progress_id, []) | 1079 callbacks = self._progress_ids.setdefault(progress_id, []) |
1080 callbacks.append((callback, errback)) | 1080 callbacks.append((callback, errback)) |
1081 | 1081 |
1082 def progressStartedHandler(self, pid, metadata, profile): | 1082 def progressStartedHandler(self, pid, metadata, profile): |
1083 log.info(u"Progress {} started".format(pid)) | 1083 log.info("Progress {} started".format(pid)) |
1084 | 1084 |
1085 def progressFinishedHandler(self, pid, metadata, profile): | 1085 def progressFinishedHandler(self, pid, metadata, profile): |
1086 log.info(u"Progress {} finished".format(pid)) | 1086 log.info("Progress {} finished".format(pid)) |
1087 try: | 1087 try: |
1088 callbacks = self._progress_ids.pop(pid) | 1088 callbacks = self._progress_ids.pop(pid) |
1089 except KeyError: | 1089 except KeyError: |
1090 pass | 1090 pass |
1091 else: | 1091 else: |
1093 if callback is not None: | 1093 if callback is not None: |
1094 callback(metadata, profile=profile) | 1094 callback(metadata, profile=profile) |
1095 self.callListeners("progressFinished", pid, metadata, profile=profile) | 1095 self.callListeners("progressFinished", pid, metadata, profile=profile) |
1096 | 1096 |
1097 def progressErrorHandler(self, pid, err_msg, profile): | 1097 def progressErrorHandler(self, pid, err_msg, profile): |
1098 log.warning(u"Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg)) | 1098 log.warning("Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg)) |
1099 try: | 1099 try: |
1100 callbacks = self._progress_ids.pop(pid) | 1100 callbacks = self._progress_ids.pop(pid) |
1101 except KeyError: | 1101 except KeyError: |
1102 pass | 1102 pass |
1103 else: | 1103 else: |
1107 self.callListeners("progressError", pid, err_msg, profile=profile) | 1107 self.callListeners("progressError", pid, err_msg, profile=profile) |
1108 | 1108 |
1109 def _subscribe_cb(self, answer, data): | 1109 def _subscribe_cb(self, answer, data): |
1110 entity, profile = data | 1110 entity, profile = data |
1111 type_ = "subscribed" if answer else "unsubscribed" | 1111 type_ = "subscribed" if answer else "unsubscribed" |
1112 self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile) | 1112 self.bridge.subscription(type_, str(entity.bare), profile_key=profile) |
1113 | 1113 |
1114 def subscribeHandler(self, type, raw_jid, profile): | 1114 def subscribeHandler(self, type, raw_jid, profile): |
1115 """Called when a subsciption management signal is received""" | 1115 """Called when a subsciption management signal is received""" |
1116 entity = jid.JID(raw_jid) | 1116 entity = jid.JID(raw_jid) |
1117 if type == "subscribed": | 1117 if type == "subscribed": |
1118 # this is a subscription confirmation, we just have to inform user | 1118 # this is a subscription confirmation, we just have to inform user |
1119 # TODO: call self.getEntityMBlog to add the new contact blogs | 1119 # TODO: call self.getEntityMBlog to add the new contact blogs |
1120 self.showDialog( | 1120 self.showDialog( |
1121 _(u"The contact {contact} has accepted your subscription").format( | 1121 _("The contact {contact} has accepted your subscription").format( |
1122 contact=entity.bare | 1122 contact=entity.bare |
1123 ), | 1123 ), |
1124 _(u"Subscription confirmation"), | 1124 _("Subscription confirmation"), |
1125 ) | 1125 ) |
1126 elif type == "unsubscribed": | 1126 elif type == "unsubscribed": |
1127 # this is a subscription refusal, we just have to inform user | 1127 # this is a subscription refusal, we just have to inform user |
1128 self.showDialog( | 1128 self.showDialog( |
1129 _(u"The contact {contact} has refused your subscription").format( | 1129 _("The contact {contact} has refused your subscription").format( |
1130 contact=entity.bare | 1130 contact=entity.bare |
1131 ), | 1131 ), |
1132 _(u"Subscription refusal"), | 1132 _("Subscription refusal"), |
1133 "error", | 1133 "error", |
1134 ) | 1134 ) |
1135 elif type == "subscribe": | 1135 elif type == "subscribe": |
1136 # this is a subscriptionn request, we have to ask for user confirmation | 1136 # this is a subscriptionn request, we have to ask for user confirmation |
1137 # TODO: use sat.stdui.ui_contact_list to display the groups selector | 1137 # TODO: use sat.stdui.ui_contact_list to display the groups selector |
1138 self.showDialog( | 1138 self.showDialog( |
1139 _( | 1139 _( |
1140 u"The contact {contact} wants to subscribe to your presence" | 1140 "The contact {contact} wants to subscribe to your presence" |
1141 u".\nDo you accept ?" | 1141 ".\nDo you accept ?" |
1142 ).format(contact=entity.bare), | 1142 ).format(contact=entity.bare), |
1143 _("Subscription confirmation"), | 1143 _("Subscription confirmation"), |
1144 "yes/no", | 1144 "yes/no", |
1145 answer_cb=self._subscribe_cb, | 1145 answer_cb=self._subscribe_cb, |
1146 answer_data=(entity, profile), | 1146 answer_data=(entity, profile), |
1147 ) | 1147 ) |
1148 | 1148 |
1149 def _debugHandler(self, action, parameters, profile): | 1149 def _debugHandler(self, action, parameters, profile): |
1150 if action == u"widgets_dump": | 1150 if action == "widgets_dump": |
1151 from pprint import pformat | 1151 from pprint import pformat |
1152 log.info(u"Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets))) | 1152 log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets))) |
1153 else: | 1153 else: |
1154 log.warning(u"Unknown debug action: {action}".format(action=action)) | 1154 log.warning("Unknown debug action: {action}".format(action=action)) |
1155 | 1155 |
1156 | 1156 |
1157 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): | 1157 def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None): |
1158 """Show a dialog to user | 1158 """Show a dialog to user |
1159 | 1159 |
1176 def showAlert(self, message): | 1176 def showAlert(self, message): |
1177 # FIXME: doesn't seems used anymore, to remove? | 1177 # FIXME: doesn't seems used anymore, to remove? |
1178 pass # FIXME | 1178 pass # FIXME |
1179 | 1179 |
1180 def dialogFailure(self, failure): | 1180 def dialogFailure(self, failure): |
1181 log.warning(u"Failure: {}".format(failure)) | 1181 log.warning("Failure: {}".format(failure)) |
1182 | 1182 |
1183 def progressIdHandler(self, progress_id, profile): | 1183 def progressIdHandler(self, progress_id, profile): |
1184 """Callback used when an action result in a progress id""" | 1184 """Callback used when an action result in a progress id""" |
1185 log.info(u"Progress ID received: {}".format(progress_id)) | 1185 log.info("Progress ID received: {}".format(progress_id)) |
1186 | 1186 |
1187 def isHidden(self): | 1187 def isHidden(self): |
1188 """Tells if the frontend window is hidden. | 1188 """Tells if the frontend window is hidden. |
1189 | 1189 |
1190 @return bool | 1190 @return bool |
1191 """ | 1191 """ |
1192 raise NotImplementedError | 1192 raise NotImplementedError |
1193 | 1193 |
1194 def paramUpdateHandler(self, name, value, namespace, profile): | 1194 def paramUpdateHandler(self, name, value, namespace, profile): |
1195 log.debug( | 1195 log.debug( |
1196 _(u"param update: [%(namespace)s] %(name)s = %(value)s") | 1196 _("param update: [%(namespace)s] %(name)s = %(value)s") |
1197 % {"namespace": namespace, "name": name, "value": value} | 1197 % {"namespace": namespace, "name": name, "value": value} |
1198 ) | 1198 ) |
1199 if (namespace, name) == ("Connection", "JabberID"): | 1199 if (namespace, name) == ("Connection", "JabberID"): |
1200 log.debug(_(u"Changing JID to %s") % value) | 1200 log.debug(_("Changing JID to %s") % value) |
1201 self.profiles[profile].whoami = jid.JID(value) | 1201 self.profiles[profile].whoami = jid.JID(value) |
1202 elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS): | 1202 elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS): |
1203 self.contact_lists[profile].showOfflineContacts(C.bool(value)) | 1203 self.contact_lists[profile].showOfflineContacts(C.bool(value)) |
1204 elif (namespace, name) == ("General", C.SHOW_EMPTY_GROUPS): | 1204 elif (namespace, name) == ("General", C.SHOW_EMPTY_GROUPS): |
1205 self.contact_lists[profile].showEmptyGroups(C.bool(value)) | 1205 self.contact_lists[profile].showEmptyGroups(C.bool(value)) |
1262 self.registerProgressCbs(progress_id, progress_cb, progress_eb) | 1262 self.registerProgressCbs(progress_id, progress_cb, progress_eb) |
1263 self.progressIdHandler(progress_id, profile) | 1263 self.progressIdHandler(progress_id, profile) |
1264 | 1264 |
1265 # we ignore metadata | 1265 # we ignore metadata |
1266 action_data = { | 1266 action_data = { |
1267 k: v for k, v in action_data.iteritems() if not k.startswith("meta_") | 1267 k: v for k, v in action_data.items() if not k.startswith("meta_") |
1268 } | 1268 } |
1269 | 1269 |
1270 if action_data: | 1270 if action_data: |
1271 raise exceptions.DataError( | 1271 raise exceptions.DataError( |
1272 u"Not all keys in action_data are managed ({keys})".format( | 1272 "Not all keys in action_data are managed ({keys})".format( |
1273 keys=", ".join(action_data.keys()) | 1273 keys=", ".join(list(action_data.keys())) |
1274 ) | 1274 ) |
1275 ) | 1275 ) |
1276 | 1276 |
1277 def _actionCb(self, data, callback, callback_id, profile): | 1277 def _actionCb(self, data, callback, callback_id, profile): |
1278 if callback is None: | 1278 if callback is None: |
1346 contact_list.setCache(entity, "avatar", path) | 1346 contact_list.setCache(entity, "avatar", path) |
1347 self.callListeners("avatar", entity, path, profile=profile) | 1347 self.callListeners("avatar", entity, path, profile=profile) |
1348 | 1348 |
1349 def _avatarGetEb(self, failure_, entity, contact_list): | 1349 def _avatarGetEb(self, failure_, entity, contact_list): |
1350 # FIXME: bridge needs a proper error handling | 1350 # FIXME: bridge needs a proper error handling |
1351 if "NotFound" in unicode(failure_): | 1351 if "NotFound" in str(failure_): |
1352 log.info(u"No avatar found for {entity}".format(entity=entity)) | 1352 log.info("No avatar found for {entity}".format(entity=entity)) |
1353 else: | 1353 else: |
1354 log.warning(u"Can't get avatar: {}".format(failure_)) | 1354 log.warning("Can't get avatar: {}".format(failure_)) |
1355 contact_list.setCache(entity, "avatar", self.getDefaultAvatar(entity)) | 1355 contact_list.setCache(entity, "avatar", self.getDefaultAvatar(entity)) |
1356 | 1356 |
1357 def getAvatar( | 1357 def getAvatar( |
1358 self, | 1358 self, |
1359 entity, | 1359 entity, |
1381 avatar = contact_list.getCache(entity, "avatar", bare_default=None) | 1381 avatar = contact_list.getCache(entity, "avatar", bare_default=None) |
1382 except exceptions.NotFound: | 1382 except exceptions.NotFound: |
1383 avatar = None | 1383 avatar = None |
1384 if avatar is None: | 1384 if avatar is None: |
1385 self.bridge.avatarGet( | 1385 self.bridge.avatarGet( |
1386 unicode(entity), | 1386 str(entity), |
1387 cache_only, | 1387 cache_only, |
1388 hash_only, | 1388 hash_only, |
1389 profile=profile, | 1389 profile=profile, |
1390 callback=lambda path: self._avatarGetCb( | 1390 callback=lambda path: self._avatarGetCb( |
1391 path, entity, contact_list, profile | 1391 path, entity, contact_list, profile |
1411 self.bridge.disconnect(profile) | 1411 self.bridge.disconnect(profile) |
1412 | 1412 |
1413 def onExit(self): | 1413 def onExit(self): |
1414 """Must be called when the frontend is terminating""" | 1414 """Must be called when the frontend is terminating""" |
1415 to_unplug = [] | 1415 to_unplug = [] |
1416 for profile, profile_manager in self.profiles.iteritems(): | 1416 for profile, profile_manager in self.profiles.items(): |
1417 if profile_manager.connected and profile_manager.autodisconnect: | 1417 if profile_manager.connected and profile_manager.autodisconnect: |
1418 # The user wants autodisconnection | 1418 # The user wants autodisconnection |
1419 self.disconnect(profile) | 1419 self.disconnect(profile) |
1420 to_unplug.append(profile) | 1420 to_unplug.append(profile) |
1421 for profile in to_unplug: | 1421 for profile in to_unplug: |