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
This commit is contained in:
Goffi 2019-08-13 19:08:41 +02:00
commit fbfe96c505

View file

@ -40,12 +40,12 @@ import time
try:
# FIXME: to be removed when an acceptable solution is here
unicode("") # XXX: unicode doesn't exist in pyjamas
str("") # XXX: unicode doesn't exist in pyjamas
except (
TypeError,
AttributeError,
): # Error raised is not the same depending on pyjsbuild options
unicode = str
str = str
class ProfileManager(object):
@ -139,7 +139,7 @@ class ProfileManager(object):
)
def _plug_profile_getFeaturesEb(self, failure):
log.error(u"Couldn't get features: {}".format(failure))
log.error("Couldn't get features: {}".format(failure))
self._plug_profile_getFeaturesCb({})
def _plug_profile_getFeaturesCb(self, features):
@ -154,14 +154,14 @@ class ProfileManager(object):
self._plug_profile_gotCachedValues({})
def _plug_profile_failedCachedValues(self, failure):
log.error(u"Couldn't get cached values: {}".format(failure))
log.error("Couldn't get cached values: {}".format(failure))
self._plug_profile_gotCachedValues({})
def _plug_profile_gotCachedValues(self, cached_values):
contact_list = self.host.contact_lists[self.profile]
# add the contact list and its listener
for entity_s, data in cached_values.iteritems():
for key, value in data.iteritems():
for entity_s, data in cached_values.items():
for key, value in data.items():
self.host.entityDataUpdatedHandler(entity_s, key, value, self.profile)
if not self.connected:
@ -203,7 +203,7 @@ class ProfileManager(object):
for contact in presences:
for res in presences[contact]:
jabber_id = (u"%s/%s" % (jid.JID(contact).bare, res)) if res else contact
jabber_id = ("%s/%s" % (jid.JID(contact).bare, res)) if res else contact
show = presences[contact][res][0]
priority = presences[contact][res][1]
statuses = presences[contact][res][2]
@ -216,7 +216,7 @@ class ProfileManager(object):
self.profile,
callback=lambda data, contact=contact: gotEntityData(data, contact),
errback=lambda failure, contact=contact: log.debug(
u"No cache data for {}".format(contact)
"No cache data for {}".format(contact)
),
)
@ -238,7 +238,7 @@ class ProfilesManager(object):
return profile in self._profiles
def __iter__(self):
return self._profiles.iterkeys()
return iter(self._profiles.keys())
def __getitem__(self, profile):
return self._profiles[profile]
@ -246,11 +246,11 @@ class ProfilesManager(object):
def __len__(self):
return len(self._profiles)
def iteritems(self):
return self._profiles.iteritems()
def items(self):
return self._profiles.items()
def itervalues(self):
return self._profiles.itervalues()
def values(self):
return self._profiles.values()
def plug(self, profile):
if profile in self._profiles:
@ -271,7 +271,7 @@ class ProfilesManager(object):
del self._profiles[profile]
def chooseOneProfile(self):
return self._profiles.keys()[0]
return list(self._profiles.keys())[0]
class QuickApp(object):
@ -350,13 +350,13 @@ class QuickApp(object):
self.ns_map = ns_map
def _namespacesGetEb(self, failure_):
log.error(_(u"Can't get namespaces map: {msg}").format(msg=failure_))
log.error(_("Can't get namespaces map: {msg}").format(msg=failure_))
def _encryptionPluginsGetCb(self, plugins):
self.encryption_plugins = plugins
def _encryptionPluginsGetEb(self, failure_):
log.warning(_(u"Can't retrieve encryption plugins: {msg}").format(msg=failure_))
log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
def onBridgeConnected(self):
self.bridge.namespacesGet(
@ -402,13 +402,13 @@ class QuickApp(object):
def _bridgeEb(self, failure):
if isinstance(failure, exceptions.BridgeExceptionNoService):
print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
print((_("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"))
print((_("Can't init bridge")))
sys.exit(1)
else:
print(_(u"Error while initialising bridge: {}".format(failure)))
print((_("Error while initialising bridge: {}".format(failure))))
@property
def current_profile(self):
@ -459,10 +459,10 @@ class QuickApp(object):
or if connection has been lost and a reconnection is needed
"""
if state:
log.debug(u"we are synchronised with server")
log.debug("we are synchronised with server")
if self.AUTO_RESYNC:
# we are resynchronising all widgets
log.debug(u"doing a full widgets resynchronisation")
log.debug("doing a full widgets resynchronisation")
for w in self.widgets:
try:
resync = w.resync
@ -474,7 +474,7 @@ class QuickApp(object):
self._sync = state
else:
log.debug(u"we have lost synchronisation with server")
log.debug("we have lost synchronisation with server")
self._sync = state
# we've lost synchronisation, all widgets must be notified
# note: this is always called independently of AUTO_RESYNC
@ -496,7 +496,7 @@ class QuickApp(object):
@param with_profile (boolean): True if the signal concerns a specific profile,
in that case the profile name has to be passed by the caller
"""
log.debug(u"registering signal {name}".format(name=function_name))
log.debug("registering signal {name}".format(name=function_name))
if handler is None:
handler = getattr(self, "{}{}".format(function_name, "Handler"))
if not with_profile:
@ -582,7 +582,7 @@ class QuickApp(object):
pass
else:
profile = kwargs.get("profile")
for listener, profiles_filter in listeners.iteritems():
for listener, profiles_filter in listeners.items():
if profile is None or not profiles_filter or profile in profiles_filter:
listener(*args, **kwargs)
@ -617,7 +617,7 @@ class QuickApp(object):
cached_signals = self.signals_cache.pop(profile, [])
for function_name, handler, args, kwargs in cached_signals:
log.debug(
u"Calling cached signal [%s] with args %s and kwargs %s"
"Calling cached signal [%s] with args %s and kwargs %s"
% (function_name, args, kwargs)
)
handler(*args, **kwargs)
@ -632,7 +632,7 @@ class QuickApp(object):
if not errback:
def errback(failure):
log.error(_(u"Can't connect profile [%s]") % failure)
log.error(_("Can't connect profile [%s]") % failure)
try:
module = failure.module
except AttributeError:
@ -790,8 +790,8 @@ class QuickApp(object):
widget.onMessageState(uid, status, profile)
def messageSend(self, to_jid, message, subject=None, mess_type="auto", extra=None, callback=None, errback=None, profile_key=C.PROF_KEY_NONE):
if not subject and not extra and (not message or message == {u'': u''}):
log.debug(u"Not sending empty message")
if not subject and not extra and (not message or message == {'': ''}):
log.debug("Not sending empty message")
return
if subject is None:
@ -812,7 +812,7 @@ class QuickApp(object):
return
self.bridge.messageSend(
unicode(to_jid),
str(to_jid),
message,
subject,
mess_type,
@ -857,8 +857,8 @@ class QuickApp(object):
def mucRoomJoinedHandler(self, room_jid_s, occupants, user_nick, subject, profile):
"""Called when a MUC room is joined"""
log.debug(
u"Room [{room_jid}] joined by {profile}, users presents:{users}".format(
room_jid=room_jid_s, profile=profile, users=occupants.keys()
"Room [{room_jid}] joined by {profile}, users presents:{users}".format(
room_jid=room_jid_s, profile=profile, users=list(occupants.keys())
)
)
room_jid = jid.JID(room_jid_s)
@ -876,7 +876,7 @@ class QuickApp(object):
def mucRoomLeftHandler(self, room_jid_s, profile):
"""Called when a MUC room is left"""
log.debug(
u"Room [%(room_jid)s] left by %(profile)s"
"Room [%(room_jid)s] left by %(profile)s"
% {"room_jid": room_jid_s, "profile": profile}
)
room_jid = jid.JID(room_jid_s)
@ -893,7 +893,7 @@ class QuickApp(object):
)
chat_widget.changeUserNick(old_nick, new_nick)
log.debug(
u"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]"
"user [%(old_nick)s] is now known as [%(new_nick)s] in room [%(room_jid)s]"
% {"old_nick": old_nick, "new_nick": new_nick, "room_jid": room_jid}
)
@ -905,7 +905,7 @@ class QuickApp(object):
)
chat_widget.setSubject(subject)
log.debug(
u"new subject for room [%(room_jid)s]: %(subject)s"
"new subject for room [%(room_jid)s]: %(subject)s"
% {"room_jid": room_jid, "subject": subject}
)
@ -974,7 +974,7 @@ class QuickApp(object):
main_notif_dict = self.profiles[profile].notifications
if entity is C.ENTITY_ALL:
selected_notifs = main_notif_dict.itervalues()
selected_notifs = iter(main_notif_dict.values())
exact_jid = False
else:
if entity is None:
@ -989,7 +989,7 @@ class QuickApp(object):
for notifs_from_select in selected_notifs:
if type_ is None:
type_notifs = notifs_from_select.itervalues()
type_notifs = iter(notifs_from_select.values())
else:
type_notifs = (notifs_from_select.get(type_, []),)
@ -1080,10 +1080,10 @@ class QuickApp(object):
callbacks.append((callback, errback))
def progressStartedHandler(self, pid, metadata, profile):
log.info(u"Progress {} started".format(pid))
log.info("Progress {} started".format(pid))
def progressFinishedHandler(self, pid, metadata, profile):
log.info(u"Progress {} finished".format(pid))
log.info("Progress {} finished".format(pid))
try:
callbacks = self._progress_ids.pop(pid)
except KeyError:
@ -1095,7 +1095,7 @@ class QuickApp(object):
self.callListeners("progressFinished", pid, metadata, profile=profile)
def progressErrorHandler(self, pid, err_msg, profile):
log.warning(u"Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg))
log.warning("Progress {pid} error: {err_msg}".format(pid=pid, err_msg=err_msg))
try:
callbacks = self._progress_ids.pop(pid)
except KeyError:
@ -1109,7 +1109,7 @@ class QuickApp(object):
def _subscribe_cb(self, answer, data):
entity, profile = data
type_ = "subscribed" if answer else "unsubscribed"
self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile)
self.bridge.subscription(type_, str(entity.bare), profile_key=profile)
def subscribeHandler(self, type, raw_jid, profile):
"""Called when a subsciption management signal is received"""
@ -1118,18 +1118,18 @@ class QuickApp(object):
# this is a subscription confirmation, we just have to inform user
# TODO: call self.getEntityMBlog to add the new contact blogs
self.showDialog(
_(u"The contact {contact} has accepted your subscription").format(
_("The contact {contact} has accepted your subscription").format(
contact=entity.bare
),
_(u"Subscription confirmation"),
_("Subscription confirmation"),
)
elif type == "unsubscribed":
# this is a subscription refusal, we just have to inform user
self.showDialog(
_(u"The contact {contact} has refused your subscription").format(
_("The contact {contact} has refused your subscription").format(
contact=entity.bare
),
_(u"Subscription refusal"),
_("Subscription refusal"),
"error",
)
elif type == "subscribe":
@ -1137,8 +1137,8 @@ class QuickApp(object):
# TODO: use sat.stdui.ui_contact_list to display the groups selector
self.showDialog(
_(
u"The contact {contact} wants to subscribe to your presence"
u".\nDo you accept ?"
"The contact {contact} wants to subscribe to your presence"
".\nDo you accept ?"
).format(contact=entity.bare),
_("Subscription confirmation"),
"yes/no",
@ -1147,11 +1147,11 @@ class QuickApp(object):
)
def _debugHandler(self, action, parameters, profile):
if action == u"widgets_dump":
if action == "widgets_dump":
from pprint import pformat
log.info(u"Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets)))
log.info("Widgets dump:\n{data}".format(data=pformat(self.widgets._widgets)))
else:
log.warning(u"Unknown debug action: {action}".format(action=action))
log.warning("Unknown debug action: {action}".format(action=action))
def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None):
@ -1178,11 +1178,11 @@ class QuickApp(object):
pass # FIXME
def dialogFailure(self, failure):
log.warning(u"Failure: {}".format(failure))
log.warning("Failure: {}".format(failure))
def progressIdHandler(self, progress_id, profile):
"""Callback used when an action result in a progress id"""
log.info(u"Progress ID received: {}".format(progress_id))
log.info("Progress ID received: {}".format(progress_id))
def isHidden(self):
"""Tells if the frontend window is hidden.
@ -1193,11 +1193,11 @@ class QuickApp(object):
def paramUpdateHandler(self, name, value, namespace, profile):
log.debug(
_(u"param update: [%(namespace)s] %(name)s = %(value)s")
_("param update: [%(namespace)s] %(name)s = %(value)s")
% {"namespace": namespace, "name": name, "value": value}
)
if (namespace, name) == ("Connection", "JabberID"):
log.debug(_(u"Changing JID to %s") % value)
log.debug(_("Changing JID to %s") % value)
self.profiles[profile].whoami = jid.JID(value)
elif (namespace, name) == ("General", C.SHOW_OFFLINE_CONTACTS):
self.contact_lists[profile].showOfflineContacts(C.bool(value))
@ -1264,13 +1264,13 @@ class QuickApp(object):
# we ignore metadata
action_data = {
k: v for k, v in action_data.iteritems() if not k.startswith("meta_")
k: v for k, v in action_data.items() if not k.startswith("meta_")
}
if action_data:
raise exceptions.DataError(
u"Not all keys in action_data are managed ({keys})".format(
keys=", ".join(action_data.keys())
"Not all keys in action_data are managed ({keys})".format(
keys=", ".join(list(action_data.keys()))
)
)
@ -1348,10 +1348,10 @@ class QuickApp(object):
def _avatarGetEb(self, failure_, entity, contact_list):
# FIXME: bridge needs a proper error handling
if "NotFound" in unicode(failure_):
log.info(u"No avatar found for {entity}".format(entity=entity))
if "NotFound" in str(failure_):
log.info("No avatar found for {entity}".format(entity=entity))
else:
log.warning(u"Can't get avatar: {}".format(failure_))
log.warning("Can't get avatar: {}".format(failure_))
contact_list.setCache(entity, "avatar", self.getDefaultAvatar(entity))
def getAvatar(
@ -1383,7 +1383,7 @@ class QuickApp(object):
avatar = None
if avatar is None:
self.bridge.avatarGet(
unicode(entity),
str(entity),
cache_only,
hash_only,
profile=profile,
@ -1413,7 +1413,7 @@ class QuickApp(object):
def onExit(self):
"""Must be called when the frontend is terminating"""
to_unplug = []
for profile, profile_manager in self.profiles.iteritems():
for profile, profile_manager in self.profiles.items():
if profile_manager.connected and profile_manager.autodisconnect:
# The user wants autodisconnection
self.disconnect(profile)