diff sat/core/sat_main.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 6959c71ab8bf
children fee60f17ebac
line wrap: on
line diff
--- a/sat/core/sat_main.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat/core/sat_main.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
@@ -66,8 +66,8 @@
         self.plugins = {}
         # map for short name to whole namespace,
         self.ns_map = {
-            u"x-data": xmpp.NS_X_DATA,
-            u"disco#info": xmpp.NS_DISCO_INFO,
+            "x-data": xmpp.NS_X_DATA,
+            "disco#info": xmpp.NS_DISCO_INFO,
         }
         # extended by plugins with registerNamespace
         self.memory = memory.Memory(self)
@@ -79,13 +79,13 @@
 
         bridge_module = dynamic_import.bridge(bridge_name)
         if bridge_module is None:
-            log.error(u"Can't find bridge module of name {}".format(bridge_name))
+            log.error("Can't find bridge module of name {}".format(bridge_name))
             sys.exit(1)
-        log.info(u"using {} bridge".format(bridge_name))
+        log.info("using {} bridge".format(bridge_name))
         try:
             self.bridge = bridge_module.Bridge()
         except exceptions.BridgeInitError:
-            log.error(u"Bridge can't be initialised, can't start SàT core")
+            log.error("Bridge can't be initialised, can't start SàT core")
             sys.exit(1)
         self.bridge.register_method("getReady", lambda: self.initialised)
         self.bridge.register_method("getVersion", lambda: self.full_version)
@@ -181,7 +181,7 @@
             try:
                 return self._version_cache
             except AttributeError:
-                self._version_cache = u"{} « {} » ({})".format(
+                self._version_cache = "{} « {} » ({})".format(
                     version, C.APP_RELEASE_NAME, utils.getRepositoryData(sat)
                 )
                 return self._version_cache
@@ -202,14 +202,14 @@
             ui_profile_manager.ProfileManager(self)
         except Exception as e:
             log.error(
-                _(u"Could not initialize backend: {reason}").format(
+                _("Could not initialize backend: {reason}").format(
                     reason=str(e).decode("utf-8", "ignore")
                 )
             )
             sys.exit(1)
         self._addBaseMenus()
         self.initialised.callback(None)
-        log.info(_(u"Backend is ready"))
+        log.info(_("Backend is ready"))
 
     def _addBaseMenus(self):
         """Add base menus"""
@@ -246,15 +246,15 @@
             except exceptions.MissingModule as e:
                 self._unimport_plugin(plugin_path)
                 log.warning(
-                    u"Can't import plugin [{path}] because of an unavailale third party "
-                    u"module:\n{msg}".format(
+                    "Can't import plugin [{path}] because of an unavailale third party "
+                    "module:\n{msg}".format(
                         path=plugin_path, msg=e
                     )
                 )
                 continue
             except exceptions.CancelError as e:
                 log.info(
-                    u"Plugin [{path}] cancelled its own import: {msg}".format(
+                    "Plugin [{path}] cancelled its own import: {msg}".format(
                         path=plugin_path, msg=e
                     )
                 )
@@ -264,7 +264,7 @@
                 import traceback
 
                 log.error(
-                    _(u"Can't import plugin [{path}]:\n{error}").format(
+                    _("Can't import plugin [{path}]:\n{error}").format(
                         path=plugin_path, error=traceback.format_exc()
                     )
                 )
@@ -274,17 +274,17 @@
             plugin_info = mod.PLUGIN_INFO
             import_name = plugin_info["import_name"]
 
-            plugin_modes = plugin_info[u"modes"] = set(
-                plugin_info.setdefault(u"modes", C.PLUG_MODE_DEFAULT)
+            plugin_modes = plugin_info["modes"] = set(
+                plugin_info.setdefault("modes", C.PLUG_MODE_DEFAULT)
             )
 
             # if the plugin is an entry point, it must work in component mode
-            if plugin_info[u"type"] == C.PLUG_TYPE_ENTRY_POINT:
+            if plugin_info["type"] == C.PLUG_TYPE_ENTRY_POINT:
                 # if plugin is an entrypoint, we cache it
                 if C.PLUG_MODE_COMPONENT not in plugin_modes:
                     log.error(
                         _(
-                            u"{type} type must be used with {mode} mode, ignoring plugin"
+                            "{type} type must be used with {mode} mode, ignoring plugin"
                         ).format(type=C.PLUG_TYPE_ENTRY_POINT, mode=C.PLUG_MODE_COMPONENT)
                     )
                     self._unimport_plugin(plugin_path)
@@ -293,8 +293,8 @@
             if import_name in plugins_to_import:
                 log.error(
                     _(
-                        u"Name conflict for import name [{import_name}], can't import "
-                        u"plugin [{name}]"
+                        "Name conflict for import name [{import_name}], can't import "
+                        "plugin [{name}]"
                     ).format(**plugin_info)
                 )
                 continue
@@ -320,7 +320,7 @@
                                is raised
         """
         if import_name in self.plugins:
-            log.debug(u"Plugin {} already imported, passing".format(import_name))
+            log.debug("Plugin {} already imported, passing".format(import_name))
             return
         if not import_name:
             import_name, (plugin_path, mod, plugin_info) = plugins_to_import.popitem()
@@ -328,10 +328,10 @@
             if not import_name in plugins_to_import:
                 if optional:
                     log.warning(
-                        _(u"Recommended plugin not found: {}").format(import_name)
+                        _("Recommended plugin not found: {}").format(import_name)
                     )
                     return
-                msg = u"Dependency not found: {}".format(import_name)
+                msg = "Dependency not found: {}".format(import_name)
                 log.error(msg)
                 raise ImportError(msg)
             plugin_path, mod, plugin_info = plugins_to_import.pop(import_name)
@@ -340,7 +340,7 @@
         for to_import in dependencies + recommendations:
             if to_import not in self.plugins:
                 log.debug(
-                    u"Recursively import dependency of [%s]: [%s]"
+                    "Recursively import dependency of [%s]: [%s]"
                     % (import_name, to_import)
                 )
                 try:
@@ -349,7 +349,7 @@
                     )
                 except ImportError as e:
                     log.warning(
-                        _(u"Can't import plugin {name}: {error}").format(
+                        _("Can't import plugin {name}: {error}").format(
                             name=plugin_info["name"], error=e
                         )
                     )
@@ -362,13 +362,13 @@
             self.plugins[import_name] = getattr(mod, plugin_info["main"])(self)
         except Exception as e:
             log.warning(
-                u'Error while loading plugin "{name}", ignoring it: {error}'.format(
+                'Error while loading plugin "{name}", ignoring it: {error}'.format(
                     name=plugin_info["name"], error=e
                 )
             )
             if optional:
                 return
-            raise ImportError(u"Error during initiation")
+            raise ImportError("Error during initiation")
         if C.bool(plugin_info.get(C.PI_HANDLER, C.BOOL_FALSE)):
             self.plugins[import_name].is_handler = True
         else:
@@ -386,7 +386,7 @@
         #       pluging depending on the unloaded one should be unloaded too
         #       for now, just a basic call on plugin.unload is done
         defers_list = []
-        for plugin in self.plugins.itervalues():
+        for plugin in self.plugins.values():
             try:
                 unload = plugin.unload
             except AttributeError:
@@ -419,7 +419,7 @@
 
         def connectProfile(__=None):
             if self.isConnected(profile):
-                log.info(_(u"already connected !"))
+                log.info(_("already connected !"))
                 return True
 
             if self.memory.isComponent(profile):
@@ -439,7 +439,7 @@
         if not self.isConnected(profile_key):
             # isConnected is checked here and not on client
             # because client is deleted when session is ended
-            log.info(_(u"not connected !"))
+            log.info(_("not connected !"))
             return defer.succeed(None)
         client = self.getClient(profile_key)
         return client.entityDisconnect()
@@ -468,7 +468,7 @@
             pass
 
         features = []
-        for import_name, plugin in self.plugins.iteritems():
+        for import_name, plugin in self.plugins.items():
             try:
                 features_d = defer.maybeDeferred(plugin.getFeatures, profile_key)
             except AttributeError:
@@ -485,14 +485,14 @@
                     ret[name] = data
                 else:
                     log.warning(
-                        u"Error while getting features for {name}: {failure}".format(
+                        "Error while getting features for {name}: {failure}".format(
                             name=name, failure=data
                         )
                     )
                     ret[name] = {}
             return ret
 
-        d_list.addCallback(buildFeatures, self.plugins.keys())
+        d_list.addCallback(buildFeatures, list(self.plugins.keys()))
         return d_list
 
     def getContacts(self, profile_key):
@@ -527,10 +527,10 @@
             self.memory.purgeProfileSession(profile)
 
     def startService(self):
-        log.info(u"Salut à toi ô mon frère !")
+        log.info("Salut à toi ô mon frère !")
 
     def stopService(self):
-        log.info(u"Salut aussi à Rantanplan")
+        log.info("Salut aussi à Rantanplan")
         return self.pluginsUnload()
 
     def run(self):
@@ -576,13 +576,13 @@
         @return: list of clients
         """
         if not profile_key:
-            raise exceptions.DataError(_(u"profile_key must not be empty"))
+            raise exceptions.DataError(_("profile_key must not be empty"))
         try:
             profile = self.memory.getProfileName(profile_key, True)
         except exceptions.ProfileUnknownError:
             return []
         if profile == C.PROF_KEY_ALL:
-            return self.profiles.values()
+            return list(self.profiles.values())
         elif profile[0] == "@":  #  only profile keys can start with "@"
             raise exceptions.ProfileKeyUnknown
         return [self.profiles[profile]]
@@ -594,9 +594,9 @@
         @param name: name of the option
         @return: unicode representation of the option
         """
-        return unicode(self.memory.getConfig(section, name, ""))
+        return str(self.memory.getConfig(section, name, ""))
 
-    def logErrback(self, failure_, msg=_(u"Unexpected error: {failure_}")):
+    def logErrback(self, failure_, msg=_("Unexpected error: {failure_}")):
         """Generic errback logging
 
         @param msg(unicode): error message ("failure_" key will be use for format)
@@ -610,7 +610,7 @@
     def registerNamespace(self, short_name, namespace):
         """associate a namespace to a short name"""
         if short_name in self.ns_map:
-            raise exceptions.ConflictError(u"this short name is already used")
+            raise exceptions.ConflictError("this short name is already used")
         self.ns_map[short_name] = namespace
 
     def getNamespaces(self):
@@ -620,7 +620,7 @@
         try:
             return self.ns_map[short_name]
         except KeyError:
-            raise exceptions.NotFound(u"namespace {short_name} is not registered"
+            raise exceptions.NotFound("namespace {short_name} is not registered"
                                       .format(short_name=short_name))
 
     def getSessionInfos(self, profile_key):
@@ -628,7 +628,7 @@
         client = self.getClient(profile_key)
         data = {
             "jid": client.jid.full(),
-            "started": unicode(int(client.started))
+            "started": str(int(client.started))
             }
         return defer.succeed(data)
 
@@ -714,9 +714,9 @@
         ret = []
         for p in plugins:
             ret.append({
-                u"name": p.name,
-                u"namespace": p.namespace,
-                u"priority": unicode(p.priority),
+                "name": p.name,
+                "namespace": p.namespace,
+                "priority": str(p.priority),
                 })
         return ret
 
@@ -740,7 +740,7 @@
             message,
             subject,
             mess_type,
-            {unicode(key): unicode(value) for key, value in extra.items()},
+            {str(key): str(value) for key, value in list(extra.items())},
         )
 
     def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
@@ -774,7 +774,7 @@
         assert profile
         to_jid = jid.JID(raw_jid)
         log.debug(
-            _(u"subsciption request [%(subs_type)s] for %(jid)s")
+            _("subsciption request [%(subs_type)s] for %(jid)s")
             % {"subs_type": subs_type, "jid": to_jid.full()}
         )
         if subs_type == "subscribe":
@@ -901,15 +901,15 @@
                 service_jid = services_jids[idx]
                 if not success:
                     log.warning(
-                        _(u"Can't find features for service {service_jid}, ignoring")
+                        _("Can't find features for service {service_jid}, ignoring")
                         .format(service_jid=service_jid.full()))
                     continue
                 if (identities is not None
                     and not set(infos.identities.keys()).issuperset(identities)):
                     continue
                 found_identities = [
-                    (cat, type_, name or u"")
-                    for (cat, type_), name in infos.identities.iteritems()
+                    (cat, type_, name or "")
+                    for (cat, type_), name in infos.identities.items()
                 ]
                 found_service[service_jid.full()] = found_identities
 
@@ -960,7 +960,7 @@
             full_jid = full_jids[idx]
             if not success:
                 log.warning(
-                    _(u"Can't retrieve {full_jid} infos, ignoring")
+                    _("Can't retrieve {full_jid} infos, ignoring")
                     .format(full_jid=full_jid.full()))
                 continue
             if infos.features.issuperset(namespaces):
@@ -969,8 +969,8 @@
                 ).issuperset(identities):
                     continue
                 found_identities = [
-                    (cat, type_, name or u"")
-                    for (cat, type_), name in infos.identities.iteritems()
+                    (cat, type_, name or "")
+                    for (cat, type_), name in infos.identities.items()
                 ]
                 found[full_jid.full()] = found_identities
 
@@ -979,7 +979,7 @@
     ## Generic HMI ##
 
     def _killAction(self, keep_id, client):
-        log.debug(u"Killing action {} for timeout".format(keep_id))
+        log.debug("Killing action {} for timeout".format(keep_id))
         client.actions[keep_id]
 
     def actionNew(
@@ -998,7 +998,7 @@
             Action will be deleted after 30 min.
         @param profile: %(doc_profile)s
         """
-        id_ = unicode(uuid.uuid4())
+        id_ = str(uuid.uuid4())
         if keep_id is not None:
             client = self.getClient(profile)
             action_timer = reactor.callLater(60 * 30, self._killAction, keep_id, client)
@@ -1012,7 +1012,7 @@
         @param profile: %(doc_profile)s
         """
         client = self.getClient(profile)
-        return [action_tuple[:-1] for action_tuple in client.actions.itervalues()]
+        return [action_tuple[:-1] for action_tuple in client.actions.values()]
 
     def registerProgressCb(
         self, progress_id, callback, metadata=None, profile=C.PROF_KEY_NONE
@@ -1022,7 +1022,7 @@
             metadata = {}
         client = self.getClient(profile)
         if progress_id in client._progress_cb:
-            raise exceptions.ConflictError(u"Progress ID is not unique !")
+            raise exceptions.ConflictError("Progress ID is not unique !")
         client._progress_cb[progress_id] = (callback, metadata)
 
     def removeProgressCb(self, progress_id, profile):
@@ -1031,11 +1031,11 @@
         try:
             del client._progress_cb[progress_id]
         except KeyError:
-            log.error(_(u"Trying to remove an unknow progress callback"))
+            log.error(_("Trying to remove an unknow progress callback"))
 
     def _progressGet(self, progress_id, profile):
         data = self.progressGet(progress_id, profile)
-        return {k: unicode(v) for k, v in data.iteritems()}
+        return {k: str(v) for k, v in data.items()}
 
     def progressGet(self, progress_id, profile):
         """Return a dict with progress information
@@ -1057,10 +1057,10 @@
 
     def _progressGetAll(self, profile_key):
         progress_all = self.progressGetAll(profile_key)
-        for profile, progress_dict in progress_all.iteritems():
-            for progress_id, data in progress_dict.iteritems():
-                for key, value in data.iteritems():
-                    data[key] = unicode(value)
+        for profile, progress_dict in progress_all.items():
+            for progress_id, data in progress_dict.items():
+                for key, value in data.items():
+                    data[key] = str(value)
         return progress_all
 
     def progressGetAllMetadata(self, profile_key):
@@ -1082,7 +1082,7 @@
             for (
                 progress_id,
                 (__, progress_metadata),
-            ) in client._progress_cb.iteritems():
+            ) in client._progress_cb.items():
                 progress_dict[progress_id] = progress_metadata
         return progress_all
 
@@ -1101,7 +1101,7 @@
             profile = client.profile
             progress_dict = {}
             progress_all[profile] = progress_dict
-            for progress_id, (progress_cb, __) in client._progress_cb.iteritems():
+            for progress_id, (progress_cb, __) in client._progress_cb.items():
                 progress_dict[progress_id] = progress_cb(progress_id, profile)
         return progress_all
 
@@ -1121,7 +1121,7 @@
             callback_id = str(uuid.uuid4())
         else:
             if callback_id in self._cb_map:
-                raise exceptions.ConflictError(_(u"id already registered"))
+                raise exceptions.ConflictError(_("id already registered"))
         self._cb_map[callback_id] = (callback, args, kwargs)
 
         if "one_shot" in kwargs:  # One Shot callback are removed after 30 min
@@ -1163,7 +1163,7 @@
             profile = self.memory.getProfileName(profile_key)
             if not profile:
                 raise exceptions.ProfileUnknownError(
-                    _(u"trying to launch action with a non-existant profile")
+                    _("trying to launch action with a non-existant profile")
                 )
         else:
             profile = client.profile
@@ -1179,7 +1179,7 @@
         try:
             callback, args, kwargs = self._cb_map[callback_id]
         except KeyError:
-            raise exceptions.DataError(u"Unknown callback id {}".format(callback_id))
+            raise exceptions.DataError("Unknown callback id {}".format(callback_id))
 
         if kwargs.get("with_data", False):
             if data is None:
@@ -1210,7 +1210,7 @@
 
     def importMenu(self, path, callback, security_limit=C.NO_SECURITY_LIMIT,
                    help_string="", type_=C.MENU_GLOBAL):
-        """register a new menu for frontends
+        r"""register a new menu for frontends
 
         @param path(iterable[unicode]): path to go to the menu
             (category/subcategory/.../item) (e.g.: ("File", "Open"))
@@ -1245,7 +1245,7 @@
 
         if callable(callback):
             callback_id = self.registerCallback(callback, with_data=True)
-        elif isinstance(callback, basestring):
+        elif isinstance(callback, str):
             # The callback is already registered
             callback_id = callback
             try:
@@ -1256,7 +1256,7 @@
         else:
             raise exceptions.DataError("Unknown callback type")
 
-        for menu_data in self._menus.itervalues():
+        for menu_data in self._menus.values():
             if menu_data["path"] == path and menu_data["type"] == type_:
                 raise exceptions.ConflictError(
                     _("A menu with the same path and type already exists")
@@ -1267,7 +1267,7 @@
 
         if menu_key in self._menus_paths:
             raise exceptions.ConflictError(
-                u"this menu path is already used: {path} ({menu_key})".format(
+                "this menu path is already used: {path} ({menu_key})".format(
                     path=path_canonical, menu_key=menu_key
                 )
             )
@@ -1300,7 +1300,7 @@
                 - help_url: link to a page with more complete documentation (TODO)
         """
         ret = []
-        for menu_id, menu_data in self._menus.iteritems():
+        for menu_id, menu_data in self._menus.items():
             type_ = menu_data["type"]
             path = menu_data["path"]
             menu_security_limit = menu_data["security_limit"]
@@ -1339,7 +1339,7 @@
             callback_id = self._menus_paths[menu_key]
         except KeyError:
             raise exceptions.NotFound(
-                u"Can't find menu {path} ({menu_type})".format(
+                "Can't find menu {path} ({menu_type})".format(
                     path=canonical_path, menu_type=menu_type
                 )
             )