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:
parent
d21a6a660f
commit
fbfe96c505
1 changed files with 6336 additions and 6398 deletions
|
|
@ -43,12 +43,12 @@ ROOM_USER_MOVED = (ROOM_USER_JOINED, ROOM_USER_LEFT)
|
|||
|
||||
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
|
||||
|
||||
# FIXME: day_format need to be settable (i18n)
|
||||
|
||||
|
|
@ -78,23 +78,20 @@ class Message(object):
|
|||
)
|
||||
# is user mentioned here ?
|
||||
if self.parent.type == C.CHAT_GROUP and not self.own_mess:
|
||||
for m in msg.itervalues():
|
||||
for m in msg.values():
|
||||
if self.parent.nick.lower() in m.lower():
|
||||
self._mention = True
|
||||
break
|
||||
self.handleMe()
|
||||
self.widgets = set() # widgets linked to this message
|
||||
|
||||
def __unicode__(self):
|
||||
return u"Message<{mess_type}> [{time}]{nick}> {message}".format(
|
||||
def __str__(self):
|
||||
return "Message<{mess_type}> [{time}]{nick}> {message}".format(
|
||||
mess_type=self.type,
|
||||
time=self.time_text,
|
||||
nick=self.nick,
|
||||
message=self.main_message)
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__().encode('utf-8', 'ignore')
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.parent.host
|
||||
|
|
@ -126,28 +123,28 @@ class Message(object):
|
|||
return self.message[""]
|
||||
except KeyError:
|
||||
try:
|
||||
lang, mess = self.message.iteritems().next()
|
||||
lang, mess = next(iter(self.message.items()))
|
||||
self.selected_lang = lang
|
||||
return mess
|
||||
except StopIteration:
|
||||
log.error(u"Can't find message for uid {}".format(self.uid))
|
||||
log.error("Can't find message for uid {}".format(self.uid))
|
||||
return ""
|
||||
|
||||
@property
|
||||
def main_message_xhtml(self):
|
||||
"""rich message"""
|
||||
xhtml = {k: v for k, v in self.extra.iteritems() if "html" in k}
|
||||
xhtml = {k: v for k, v in self.extra.items() if "html" in k}
|
||||
if xhtml:
|
||||
# FIXME: we only return first found value for now
|
||||
return next(xhtml.itervalues())
|
||||
return next(iter(xhtml.values()))
|
||||
|
||||
@property
|
||||
def time_text(self):
|
||||
"""Return timestamp in a nicely formatted way"""
|
||||
# if the message was sent before today, we print the full date
|
||||
timestamp = time.localtime(self.timestamp)
|
||||
time_format = u"%c" if timestamp < self.parent.day_change else u"%H:%M"
|
||||
return time.strftime(time_format, timestamp).decode(getlocale()[1] or "utf-8")
|
||||
time_format = "%c" if timestamp < self.parent.day_change else "%H:%M"
|
||||
return time.strftime(time_format, timestamp)
|
||||
|
||||
@property
|
||||
def avatar(self):
|
||||
|
|
@ -162,7 +159,7 @@ class Message(object):
|
|||
try:
|
||||
return self.extra["user_nick"]
|
||||
except KeyError:
|
||||
log.error(u"extra data is missing user nick for uid {}".format(self.uid))
|
||||
log.error("extra data is missing user nick for uid {}".format(self.uid))
|
||||
return ""
|
||||
# FIXME: converted getSpecials to list for pyjamas
|
||||
if self.parent.type == C.CHAT_GROUP or entity in list(
|
||||
|
|
@ -197,8 +194,8 @@ class Message(object):
|
|||
# TODO: XHTML-IM /me are not handled
|
||||
me = False
|
||||
# we need to check /me for every message
|
||||
for m in self.message.itervalues():
|
||||
if m.startswith(u"/me "):
|
||||
for m in self.message.values():
|
||||
if m.startswith("/me "):
|
||||
me = True
|
||||
else:
|
||||
me = False
|
||||
|
|
@ -207,8 +204,8 @@ class Message(object):
|
|||
self.type = C.MESS_TYPE_INFO
|
||||
self.extra["info_type"] = "me"
|
||||
nick = self.nick
|
||||
for lang, mess in self.message.iteritems():
|
||||
self.message[lang] = u"* " + nick + mess[3:]
|
||||
for lang, mess in self.message.items():
|
||||
self.message[lang] = "* " + nick + mess[3:]
|
||||
|
||||
|
||||
class MessageWidget(object):
|
||||
|
|
@ -245,7 +242,7 @@ class Occupant(object):
|
|||
@property
|
||||
def jid(self):
|
||||
"""jid in the room"""
|
||||
return jid.JID(u"{}/{}".format(self.parent.target.bare, self.nick))
|
||||
return jid.JID("{}/{}".format(self.parent.target.bare, self.nick))
|
||||
|
||||
@property
|
||||
def real_jid(self):
|
||||
|
|
@ -295,10 +292,10 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
if type_ == C.CHAT_GROUP:
|
||||
if target.resource:
|
||||
raise exceptions.InternalError(
|
||||
u"a group chat entity can't have a resource"
|
||||
"a group chat entity can't have a resource"
|
||||
)
|
||||
if nick is None:
|
||||
raise exceptions.InternalError(u"nick must not be None for group chat")
|
||||
raise exceptions.InternalError("nick must not be None for group chat")
|
||||
|
||||
self.nick = nick
|
||||
self.occupants = {}
|
||||
|
|
@ -306,7 +303,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
else:
|
||||
if occupants is not None or nick is not None:
|
||||
raise exceptions.InternalError(
|
||||
u"only group chat can have occupants or nick"
|
||||
"only group chat can have occupants or nick"
|
||||
)
|
||||
self.messages = OrderedDict() # key: uid, value: Message instance
|
||||
self.games = {} # key=game name (unicode), value=instance of quick_games.RoomGame
|
||||
|
|
@ -334,25 +331,25 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
# FIXME: we don't use getter/setter here because of pyjamas
|
||||
# TODO: use proper getter/setter once we get rid of pyjamas
|
||||
if self._locked:
|
||||
log.warning(u"{wid} is already locked!".format(wid=self))
|
||||
log.warning("{wid} is already locked!".format(wid=self))
|
||||
return
|
||||
self._locked = True
|
||||
# messageNew signals are cached when locked
|
||||
self._cache = OrderedDict()
|
||||
log.debug(u"{wid} is now locked".format(wid=self))
|
||||
log.debug("{wid} is now locked".format(wid=self))
|
||||
|
||||
def setUnlocked(self):
|
||||
if not self._locked:
|
||||
log.debug(u"{wid} was already unlocked".format(wid=self))
|
||||
log.debug("{wid} was already unlocked".format(wid=self))
|
||||
return
|
||||
self._locked = False
|
||||
for uid, data in self._cache.iteritems():
|
||||
for uid, data in self._cache.items():
|
||||
if uid not in self.messages:
|
||||
self.messageNew(*data)
|
||||
else:
|
||||
log.debug(u"discarding message already in history: {data}, ".format(data=data))
|
||||
log.debug("discarding message already in history: {data}, ".format(data=data))
|
||||
del self._cache
|
||||
log.debug(u"{wid} is now unlocked".format(wid=self))
|
||||
log.debug("{wid} is now unlocked".format(wid=self))
|
||||
|
||||
def postInit(self):
|
||||
"""Method to be called by frontend after widget is initialised
|
||||
|
|
@ -404,8 +401,8 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
if self._resync_lock:
|
||||
return
|
||||
self._resync_lock = True
|
||||
log.debug(u"resynchronising {self}".format(self=self))
|
||||
for mess in reversed(self.messages.values()):
|
||||
log.debug("resynchronising {self}".format(self=self))
|
||||
for mess in reversed(list(self.messages.values())):
|
||||
if mess.type == C.MESS_TYPE_INFO:
|
||||
continue
|
||||
last_message = mess
|
||||
|
|
@ -417,7 +414,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
if self.type == C.CHAT_GROUP:
|
||||
self.occupantsClear()
|
||||
self.host.bridge.mucOccupantsGet(
|
||||
unicode(self.target), self.profile, callback=self.updateOccupants,
|
||||
str(self.target), self.profile, callback=self.updateOccupants,
|
||||
errback=log.error)
|
||||
self.historyPrint(
|
||||
size=C.HISTORY_LIMIT_NONE,
|
||||
|
|
@ -427,15 +424,15 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
|
||||
## Widget management ##
|
||||
|
||||
def __unicode__(self):
|
||||
return u"Chat Widget [target: {}, type: {}, profile: {}]".format(
|
||||
def __str__(self):
|
||||
return "Chat Widget [target: {}, type: {}, profile: {}]".format(
|
||||
self.target, self.type, self.profile
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def getWidgetHash(target, profiles):
|
||||
profile = list(profiles)[0]
|
||||
return profile + "\n" + unicode(target.bare)
|
||||
return profile + "\n" + str(target.bare)
|
||||
|
||||
@staticmethod
|
||||
def getPrivateHash(target, profile):
|
||||
|
|
@ -443,7 +440,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
|
||||
This method should be used with force_hash to get unique widget for private MUC conversations
|
||||
"""
|
||||
return (unicode(profile), target)
|
||||
return (str(profile), target)
|
||||
|
||||
def addTarget(self, target):
|
||||
super(QuickChat, self).addTarget(target)
|
||||
|
|
@ -456,7 +453,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
"""copy important attribute for a new widget"""
|
||||
kwargs["type_"] = self.type
|
||||
if self.type == C.CHAT_GROUP:
|
||||
kwargs["occupants"] = {o.nick: o.data for o in self.occupants.itervalues()}
|
||||
kwargs["occupants"] = {o.nick: o.data for o in self.occupants.values()}
|
||||
kwargs["subject"] = self.subject
|
||||
try:
|
||||
kwargs["nick"] = self.nick
|
||||
|
|
@ -492,7 +489,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
def setOccupants(self, occupants):
|
||||
"""Set the whole list of occupants"""
|
||||
assert len(self.occupants) == 0
|
||||
for nick, data in occupants.iteritems():
|
||||
for nick, data in occupants.items():
|
||||
# XXX: this log is disabled because it's really too verbose
|
||||
# but kept commented as it may be useful for debugging
|
||||
# log.debug(u"adding occupant {nick} to {room}".format(
|
||||
|
|
@ -510,12 +507,12 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
updated_occupants = set(occupants)
|
||||
left_occupants = local_occupants - updated_occupants
|
||||
joined_occupants = updated_occupants - local_occupants
|
||||
log.debug(u"updating occupants for {room}:\n"
|
||||
u"left: {left_occupants}\n"
|
||||
u"joined: {joined_occupants}"
|
||||
log.debug("updating occupants for {room}:\n"
|
||||
"left: {left_occupants}\n"
|
||||
"joined: {joined_occupants}"
|
||||
.format(room=self.target,
|
||||
left_occupants=u", ".join(left_occupants),
|
||||
joined_occupants=u", ".join(joined_occupants)))
|
||||
left_occupants=", ".join(left_occupants),
|
||||
joined_occupants=", ".join(joined_occupants)))
|
||||
for nick in left_occupants:
|
||||
self.removeUser(occupants[nick])
|
||||
for nick in joined_occupants:
|
||||
|
|
@ -533,7 +530,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
try:
|
||||
occupant = self.occupants.pop(nick)
|
||||
except KeyError:
|
||||
log.warning(u"Trying to remove an unknown occupant: {}".format(nick))
|
||||
log.warning("Trying to remove an unknown occupant: {}".format(nick))
|
||||
else:
|
||||
return occupant
|
||||
|
||||
|
|
@ -602,12 +599,12 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
if filters is None:
|
||||
filters = {}
|
||||
if size == 0:
|
||||
log.debug(u"Empty history requested, skipping")
|
||||
log.debug("Empty history requested, skipping")
|
||||
self._onHistoryPrinted()
|
||||
return
|
||||
log_msg = _(u"now we print the history")
|
||||
log_msg = _("now we print the history")
|
||||
if size != C.HISTORY_LIMIT_DEFAULT:
|
||||
log_msg += _(u" ({} messages)".format(size))
|
||||
log_msg += _(" ({} messages)".format(size))
|
||||
log.debug(log_msg)
|
||||
|
||||
if self.type == C.CHAT_ONE2ONE:
|
||||
|
|
@ -661,17 +658,17 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
callback()
|
||||
|
||||
def _historyGetEb(err):
|
||||
log.error(_(u"Can't get history: {}").format(err))
|
||||
log.error(_("Can't get history: {}").format(err))
|
||||
self._onHistoryPrinted()
|
||||
if callback is not None:
|
||||
callback()
|
||||
|
||||
self.host.bridge.historyGet(
|
||||
unicode(self.host.profiles[profile].whoami.bare),
|
||||
unicode(target),
|
||||
str(self.host.profiles[profile].whoami.bare),
|
||||
str(target),
|
||||
size,
|
||||
True,
|
||||
{k: unicode(v) for k,v in filters.iteritems()},
|
||||
{k: str(v) for k,v in filters.items()},
|
||||
profile,
|
||||
callback=_historyGetCb,
|
||||
errback=_historyGetEb,
|
||||
|
|
@ -683,7 +680,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
self.messageEncryptionStarted(session_data)
|
||||
|
||||
def messageEncryptionGetEb(self, failure_):
|
||||
log.error(_(u"Can't get encryption state: {reason}").format(reason=failure_))
|
||||
log.error(_("Can't get encryption state: {reason}").format(reason=failure_))
|
||||
|
||||
def getEncryptionState(self):
|
||||
"""Retrieve encryption state with current target.
|
||||
|
|
@ -693,7 +690,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
"""
|
||||
if self.type == C.CHAT_GROUP:
|
||||
return
|
||||
self.host.bridge.messageEncryptionGet(unicode(self.target.bare), self.profile,
|
||||
self.host.bridge.messageEncryptionGet(str(self.target.bare), self.profile,
|
||||
callback=self.messageEncryptionGetCb,
|
||||
errback=self.messageEncryptionGetEb)
|
||||
|
||||
|
|
@ -715,7 +712,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
return
|
||||
|
||||
if not msg and not subject and type_ != C.MESS_TYPE_INFO:
|
||||
log.warning(u"Received an empty message for uid {}".format(uid))
|
||||
log.warning("Received an empty message for uid {}".format(uid))
|
||||
return
|
||||
|
||||
if self.type == C.CHAT_GROUP:
|
||||
|
|
@ -734,7 +731,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
pass
|
||||
else:
|
||||
user_data = {
|
||||
k[5:]: v for k, v in extra.iteritems() if k.startswith("user_")
|
||||
k[5:]: v for k, v in extra.items() if k.startswith("user_")
|
||||
}
|
||||
if info_type == ROOM_USER_JOINED:
|
||||
self.addUser(user_data)
|
||||
|
|
@ -747,18 +744,18 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
self.messages[uid] = message
|
||||
|
||||
if "received_timestamp" in extra:
|
||||
log.warning(u"Delayed message received after history, this should not happen")
|
||||
log.warning("Delayed message received after history, this should not happen")
|
||||
self.createMessage(message)
|
||||
|
||||
def messageEncryptionStarted(self, session_data):
|
||||
self.encrypted = True
|
||||
log.debug(_(u"message encryption started with {target} using {encryption}").format(
|
||||
target=self.target, encryption=session_data[u'name']))
|
||||
log.debug(_("message encryption started with {target} using {encryption}").format(
|
||||
target=self.target, encryption=session_data['name']))
|
||||
|
||||
def messageEncryptionStopped(self, session_data):
|
||||
self.encrypted = False
|
||||
log.debug(_(u"message encryption stopped with {target} (was using {encryption})")
|
||||
.format(target=self.target, encryption=session_data[u'name']))
|
||||
log.debug(_("message encryption stopped with {target} (was using {encryption})")
|
||||
.format(target=self.target, encryption=session_data['name']))
|
||||
|
||||
def createMessage(self, message, append=False):
|
||||
"""Must be implemented by frontend to create and show a new message widget
|
||||
|
|
@ -810,12 +807,12 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
count = wid.reentered_count = 1
|
||||
nick = wid.mess_data.nick
|
||||
if message.info_type == ROOM_USER_LEFT:
|
||||
wid.message = _(u"<= {nick} has left the room ({count})").format(
|
||||
wid.message = _("<= {nick} has left the room ({count})").format(
|
||||
nick=nick, count=count
|
||||
)
|
||||
else:
|
||||
wid.message = _(
|
||||
u"<=> {nick} re-entered the room ({count})"
|
||||
"<=> {nick} re-entered the room ({count})"
|
||||
).format(nick=nick, count=count)
|
||||
wid.reentered_count += 1
|
||||
return True
|
||||
|
|
@ -845,7 +842,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
This change the subject on the room itself (i.e. via XMPP),
|
||||
while setSubject change the subject of this widget
|
||||
"""
|
||||
self.host.bridge.mucSubject(unicode(self.target), new_subject, self.profile)
|
||||
self.host.bridge.mucSubject(str(self.target), new_subject, self.profile)
|
||||
|
||||
def addGamePanel(self, widget):
|
||||
"""Insert a game panel to this Chat dialog.
|
||||
|
|
@ -879,7 +876,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
self.occupants[nick].state = state
|
||||
except KeyError:
|
||||
log.warning(
|
||||
u"{nick} not found in {room}, ignoring new chat state".format(
|
||||
"{nick} not found in {room}, ignoring new chat state".format(
|
||||
nick=nick, room=self.target.bare
|
||||
)
|
||||
)
|
||||
|
|
@ -902,7 +899,7 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
# entity is not here anymore
|
||||
pass
|
||||
|
||||
for m in self.messages.values():
|
||||
for m in list(self.messages.values()):
|
||||
if m.nick == entity.resource:
|
||||
for w in m.widgets:
|
||||
w.update({"avatar": filename})
|
||||
|
|
@ -911,8 +908,8 @@ class QuickChat(quick_widgets.QuickWidget):
|
|||
entity.bare == self.target.bare
|
||||
or entity.bare == self.host.profiles[profile].whoami.bare
|
||||
):
|
||||
log.info(u"avatar updated for {}".format(entity))
|
||||
for m in self.messages.values():
|
||||
log.info("avatar updated for {}".format(entity))
|
||||
for m in list(self.messages.values()):
|
||||
if m.from_jid.bare == entity.bare:
|
||||
for w in m.widgets:
|
||||
w.update({"avatar": filename})
|
||||
|
|
|
|||
Loading…
Reference in a new issue