diff sat/memory/disco.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 b6abf8af87db
children fee60f17ebac
line wrap: on
line diff
--- a/sat/memory/disco.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/memory/disco.py	Tue Aug 13 19:08:41 2019 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 # SAT: a jabber client
@@ -50,11 +50,11 @@
         assert isinstance(identity, disco.DiscoIdentity)
         self.category = identity.category.encode("utf-8")
         self.idType = identity.type.encode("utf-8")
-        self.name = identity.name.encode("utf-8") if identity.name else ""
-        self.lang = lang.encode("utf-8") if lang is not None else ""
+        self.name = identity.name.encode("utf-8") if identity.name else b""
+        self.lang = lang.encode("utf-8") if lang is not None else b""
 
-    def __str__(self):
-        return "%s/%s/%s/%s" % (self.category, self.idType, self.lang, self.name)
+    def __bytes__(self):
+        return b"%s/%s/%s/%s" % (self.category, self.idType, self.lang, self.name)
 
 
 class HashManager(object):
@@ -74,7 +74,7 @@
 
     def __setitem__(self, hash_, disco_info):
         if hash_ in self.hashes:
-            log.debug(u"ignoring hash set: it is already known")
+            log.debug("ignoring hash set: it is already known")
             return
         self.hashes[hash_] = disco_info
         self.persistent[hash_] = disco_info.toElement().toXml()
@@ -84,19 +84,19 @@
 
     def load(self):
         def fillHashes(hashes):
-            for hash_, xml in hashes.iteritems():
+            for hash_, xml in hashes.items():
                 element = xml_tools.ElementParser()(xml)
                 disco_info = disco.DiscoInfo.fromElement(element)
                 if not disco_info.features and not disco_info.identities:
                     log.warning(
                         _(
-                            u"no feature/identity found in disco element (hash: {cap_hash}), ignoring: {xml}"
+                            "no feature/identity found in disco element (hash: {cap_hash}), ignoring: {xml}"
                         ).format(cap_hash=hash_, xml=xml)
                     )
                 else:
                     self.hashes[hash_] = disco_info
 
-            log.info(u"Disco hashes loaded")
+            log.info("Disco hashes loaded")
 
         d = self.persistent.load()
         d.addCallback(fillHashes)
@@ -116,7 +116,7 @@
         return self.hashes.load()
 
     @defer.inlineCallbacks
-    def hasFeature(self, client, feature, jid_=None, node=u""):
+    def hasFeature(self, client, feature, jid_=None, node=""):
         """Tell if an entity has the required feature
 
         @param feature: feature namespace
@@ -128,7 +128,7 @@
         defer.returnValue(feature in disco_infos.features)
 
     @defer.inlineCallbacks
-    def checkFeature(self, client, feature, jid_=None, node=u""):
+    def checkFeature(self, client, feature, jid_=None, node=""):
         """Like hasFeature, but raise an exception is feature is not Found
 
         @param feature: feature namespace
@@ -142,7 +142,7 @@
             raise failure.Failure(exceptions.FeatureNotFound)
 
     @defer.inlineCallbacks
-    def checkFeatures(self, client, features, jid_=None, identity=None, node=u""):
+    def checkFeatures(self, client, features, jid_=None, identity=None, node=""):
         """Like checkFeature, but check several features at once, and check also identity
 
         @param features(iterable[unicode]): features to check
@@ -159,7 +159,7 @@
         if identity is not None and identity not in disco_infos.identities:
             raise failure.Failure(exceptions.FeatureNotFound())
 
-    def getInfos(self, client, jid_=None, node=u"", use_cache=True):
+    def getInfos(self, client, jid_=None, node="", use_cache=True):
         """get disco infos from jid_, filling capability hash if needed
 
         @param jid_: jid of the target, or None for profile's server
@@ -188,16 +188,16 @@
 
             def infosEb(fail):
                 if fail.check(defer.CancelledError):
-                    reason = u"request time-out"
+                    reason = "request time-out"
                     fail = failure.Failure(exceptions.TimeOutError(fail.message))
                 else:
                     try:
-                        reason = unicode(fail.value)
+                        reason = str(fail.value)
                     except AttributeError:
-                        reason = unicode(fail)
+                        reason = str(fail)
 
                 log.warning(
-                    u"Error while requesting disco infos from {jid}: {reason}".format(
+                    "Error while requesting disco infos from {jid}: {reason}".format(
                         jid=jid_.full(), reason=reason
                     )
                 )
@@ -218,7 +218,7 @@
             return defer.succeed(disco_infos)
 
     @defer.inlineCallbacks
-    def getItems(self, client, jid_=None, node=u"", use_cache=True):
+    def getItems(self, client, jid_=None, node="", use_cache=True):
         """get disco items from jid_, cache them for our own server
 
         @param jid_(jid.JID): jid of the target, or None for profile's server
@@ -236,12 +236,12 @@
                 items = self.host.memory.getEntityData(
                     jid_, ["DISCO_ITEMS"], client.profile
                 )["DISCO_ITEMS"]
-                log.debug(u"[%s] disco items are in cache" % jid_.full())
+                log.debug("[%s] disco items are in cache" % jid_.full())
                 if not use_cache:
                     # we ignore cache, so we pretend we haven't found it
                     raise KeyError
             except (KeyError, exceptions.UnknownEntityError):
-                log.debug(u"Caching [%s] disco items" % jid_.full())
+                log.debug("Caching [%s] disco items" % jid_.full())
                 items = yield client.disco.requestItems(jid_, nodeIdentifier=node)
                 self.host.memory.updateEntityData(
                     jid_, "DISCO_ITEMS", items, profile_key=client.profile
@@ -251,7 +251,7 @@
                 items = yield client.disco.requestItems(jid_, nodeIdentifier=node)
             except StanzaError as e:
                 log.warning(
-                    u"Error while requesting items for {jid}: {reason}".format(
+                    "Error while requesting items for {jid}: {reason}".format(
                         jid=jid_.full(), reason=e.condition
                     )
                 )
@@ -262,7 +262,7 @@
     def _infosEb(self, failure_, entity_jid):
         failure_.trap(StanzaError)
         log.warning(
-            _(u"Error while requesting [%(jid)s]: %(error)s")
+            _("Error while requesting [%(jid)s]: %(error)s")
             % {"jid": entity_jid.full(), "error": failure_.getErrorMessage()}
         )
 
@@ -326,7 +326,7 @@
 
         def infosCb(infos, entity):
             if entity is None:
-                log.warning(_(u"received an item without jid"))
+                log.warning(_("received an item without jid"))
                 return
             if identity is not None and identity not in infos.identities:
                 return
@@ -367,8 +367,8 @@
         byte_identities.sort(key=lambda i: i.idType)
         byte_identities.sort(key=lambda i: i.category)
         for identity in byte_identities:
-            s.append(str(identity))
-            s.append("<")
+            s.append(bytes(identity))
+            s.append(b"<")
         # features
         byte_features = [
             service.encode("utf-8")
@@ -378,32 +378,32 @@
         byte_features.sort()  # XXX: the default sort has the same behaviour as the requested RFC 4790 i;octet sort
         for feature in byte_features:
             s.append(feature)
-            s.append("<")
+            s.append(b"<")
 
         # extensions
-        ext = services.extensions.values()
+        ext = list(services.extensions.values())
         ext.sort(key=lambda f: f.formNamespace.encode('utf-8'))
         for extension in ext:
             s.append(extension.formNamespace.encode('utf-8'))
-            s.append("<")
+            s.append(b"<")
             fields = extension.fieldList
             fields.sort(key=lambda f: f.var.encode('utf-8'))
             for field in fields:
                 s.append(field.var.encode('utf-8'))
-                s.append("<")
+                s.append(b"<")
                 values = [v.encode('utf-8') for v in field.values]
                 values.sort()
                 for value in values:
                     s.append(value)
-                    s.append("<")
+                    s.append(b"<")
 
-        cap_hash = b64encode(sha1("".join(s)).digest())
-        log.debug(_(u"Capability hash generated: [{cap_hash}]").format(cap_hash=cap_hash))
+        cap_hash = b64encode(sha1(b"".join(s)).digest()).decode('utf-8')
+        log.debug(_("Capability hash generated: [{cap_hash}]").format(cap_hash=cap_hash))
         return cap_hash
 
     @defer.inlineCallbacks
     def _discoInfos(
-        self, entity_jid_s, node=u"", use_cache=True, profile_key=C.PROF_KEY_NONE
+        self, entity_jid_s, node="", use_cache=True, profile_key=C.PROF_KEY_NONE
     ):
         """ Discovery method for the bridge
         @param entity_jid_s: entity we want to discover
@@ -417,7 +417,7 @@
         disco_infos = yield self.getInfos(client, entity, node, use_cache)
         extensions = {}
         # FIXME: should extensions be serialised using tools.common.data_format?
-        for form_type, form in disco_infos.extensions.items():
+        for form_type, form in list(disco_infos.extensions.items()):
             fields = []
             for field in form.fieldList:
                 data = {"type": field.fieldType}
@@ -427,7 +427,7 @@
                         data[attr] = value
 
                 values = [field.value] if field.value is not None else field.values
-                if field.fieldType == u"boolean":
+                if field.fieldType == "boolean":
                     values = [C.boolConst(v) for v in values]
                 fields.append((data, values))
 
@@ -436,7 +436,7 @@
         defer.returnValue((
             disco_infos.features,
             [(cat, type_, name or "")
-             for (cat, type_), name in disco_infos.identities.items()],
+             for (cat, type_), name in list(disco_infos.identities.items())],
             extensions))
 
     def items2tuples(self, disco_items):
@@ -447,13 +447,13 @@
         """
         for item in disco_items:
             if not item.entity:
-                log.warning(_(u"invalid item (no jid)"))
+                log.warning(_("invalid item (no jid)"))
                 continue
             yield (item.entity.full(), item.nodeIdentifier or "", item.name or "")
 
     @defer.inlineCallbacks
     def _discoItems(
-        self, entity_jid_s, node=u"", use_cache=True, profile_key=C.PROF_KEY_NONE
+        self, entity_jid_s, node="", use_cache=True, profile_key=C.PROF_KEY_NONE
     ):
         """ Discovery method for the bridge