diff sat_frontends/jp/cmd_blog.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 d603550d5e99
children fee60f17ebac
line wrap: on
line diff
--- a/sat_frontends/jp/cmd_blog.py	Wed Jul 31 11:31:22 2019 +0200
+++ b/sat_frontends/jp/cmd_blog.py	Tue Aug 13 19:08:41 2019 +0200
@@ -18,7 +18,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import base
+from . import base
 from sat.core.i18n import _
 from sat_frontends.jp.constants import Const as C
 from sat_frontends.jp import common
@@ -26,7 +26,7 @@
 from sat.tools.common import data_objects
 from sat.tools.common import uri
 from sat.tools import config
-from ConfigParser import NoSectionError, NoOptionError
+from configparser import NoSectionError, NoOptionError
 from functools import partial
 import json
 import sys
@@ -40,7 +40,7 @@
 
 __commands__ = ["Blog"]
 
-SYNTAX_XHTML = u"xhtml"
+SYNTAX_XHTML = "xhtml"
 # extensions to use with known syntaxes
 SYNTAX_EXT = {
     # FIXME: default syntax doesn't sounds needed, there should always be a syntax set
@@ -51,8 +51,8 @@
 }
 
 
-CONF_SYNTAX_EXT = u"syntax_ext_dict"
-BLOG_TMP_DIR = u"blog"
+CONF_SYNTAX_EXT = "syntax_ext_dict"
+BLOG_TMP_DIR = "blog"
 # key to remove from metadata tmp file if they exist
 KEY_TO_REMOVE_METADATA = (
     "id",
@@ -66,24 +66,24 @@
 URL_REDIRECT_PREFIX = "url_redirect_"
 INOTIFY_INSTALL = '"pip install inotify"'
 MB_KEYS = (
-    u"id",
-    u"url",
-    u"atom_id",
-    u"updated",
-    u"published",
-    u"language",
-    u"comments",  # this key is used for all comments* keys
-    u"tags",  # this key is used for all tag* keys
-    u"author",
-    u"author_jid",
-    u"author_email",
-    u"author_jid_verified",
-    u"content",
-    u"content_xhtml",
-    u"title",
-    u"title_xhtml",
+    "id",
+    "url",
+    "atom_id",
+    "updated",
+    "published",
+    "language",
+    "comments",  # this key is used for all comments* keys
+    "tags",  # this key is used for all tag* keys
+    "author",
+    "author_jid",
+    "author_email",
+    "author_jid_verified",
+    "content",
+    "content_xhtml",
+    "title",
+    "title_xhtml",
 )
-OUTPUT_OPT_NO_HEADER = u"no-header"
+OUTPUT_OPT_NO_HEADER = "no-header"
 
 
 def guessSyntaxFromPath(host, sat_conf, path):
@@ -96,7 +96,7 @@
     # we first try to guess syntax with extension
     ext = os.path.splitext(path)[1][1:]  # we get extension without the '.'
     if ext:
-        for k, v in SYNTAX_EXT.iteritems():
+        for k, v in SYNTAX_EXT.items():
             if k and ext == v:
                 return k
 
@@ -117,32 +117,30 @@
 
     def add_parser_options(self):
         self.parser.add_argument(
-            "-T", "--title", type=base.unicode_decoder, help=_(u"title of the item")
+            "-T", "--title", help=_("title of the item")
         )
         self.parser.add_argument(
             "-t",
             "--tag",
-            type=base.unicode_decoder,
             action="append",
-            help=_(u"tag (category) of your item"),
+            help=_("tag (category) of your item"),
         )
 
         comments_group = self.parser.add_mutually_exclusive_group()
         comments_group.add_argument(
             "-C", "--comments", action="store_const", const=True, dest="comments",
-            help=_(u"enable comments (default: comments not enabled except if they "
-                   u"already exist)")
+            help=_("enable comments (default: comments not enabled except if they "
+                   "already exist)")
         )
         comments_group.add_argument(
             "--no-comments", action="store_const", const=False, dest="comments",
-            help=_(u"disable comments (will remove comments node if it exist)")
+            help=_("disable comments (will remove comments node if it exist)")
         )
 
         self.parser.add_argument(
             "-S",
             "--syntax",
-            type=base.unicode_decoder,
-            help=_(u"syntax to use (default: get profile's default syntax)"),
+            help=_("syntax to use (default: get profile's default syntax)"),
         )
 
     def setMbDataContent(self, content, mb_data):
@@ -164,7 +162,7 @@
         if self.args.comments is not None:
             mb_data["allow_comments"] = self.args.comments
         if self.args.tag:
-            mb_data[u'tags'] = self.args.tag
+            mb_data['tags'] = self.args.tag
         if self.args.title is not None:
             mb_data["title"] = self.args.title
 
@@ -177,7 +175,7 @@
             "set",
             use_pubsub=True,
             pubsub_flags={C.SINGLE_ITEM},
-            help=_(u"publish a new blog item or update an existing one"),
+            help=_("publish a new blog item or update an existing one"),
         )
         BlogPublishCommon.__init__(self)
         self.need_loop = True
@@ -186,7 +184,7 @@
         BlogPublishCommon.add_parser_options(self)
 
     def mbSendCb(self):
-        self.disp(u"Item published")
+        self.disp("Item published")
         self.host.quit(C.EXIT_OK)
 
     def start(self):
@@ -207,14 +205,14 @@
             callback=self.exitCb,
             errback=partial(
                 self.errback,
-                msg=_(u"can't send item: {}"),
+                msg=_("can't send item: {}"),
                 exit_code=C.EXIT_BRIDGE_ERRBACK,
             ),
         )
 
 
 class Get(base.CommandBase):
-    TEMPLATE = u"blog/articles.html"
+    TEMPLATE = "blog/articles.html"
 
     def __init__(self, host):
         extra_outputs = {"default": self.default_output, "fancy": self.fancy_output}
@@ -227,7 +225,7 @@
             pubsub_flags={C.MULTI_ITEMS},
             use_output=C.OUTPUT_COMPLEX,
             extra_outputs=extra_outputs,
-            help=_(u"get blog item(s)"),
+            help=_("get blog item(s)"),
         )
         self.need_loop = True
 
@@ -236,34 +234,33 @@
         self.parser.add_argument(
             "-k",
             "--key",
-            type=base.unicode_decoder,
             action="append",
             dest="keys",
-            help=_(u"microblog data key(s) to display (default: depend of verbosity)"),
+            help=_("microblog data key(s) to display (default: depend of verbosity)"),
         )
         # TODO: add MAM filters
 
     def template_data_mapping(self, data):
-        return {u"items": data_objects.BlogItems(data, deserialise=False)}
+        return {"items": data_objects.BlogItems(data, deserialise=False)}
 
     def format_comments(self, item, keys):
         comments_data = data_format.dict2iterdict(
-            u"comments", item, (u"node", u"service"), pop=True
+            "comments", item, ("node", "service"), pop=True
         )
         lines = []
         for data in comments_data:
-            lines.append(data[u"comments"])
-            for k in (u"node", u"service"):
+            lines.append(data["comments"])
+            for k in ("node", "service"):
                 if OUTPUT_OPT_NO_HEADER in self.args.output_opts:
-                    header = u""
+                    header = ""
                 else:
-                    header = C.A_HEADER + k + u": " + A.RESET
+                    header = C.A_HEADER + k + ": " + A.RESET
                 lines.append(header + data[k])
-        return u"\n".join(lines)
+        return "\n".join(lines)
 
     def format_tags(self, item, keys):
-        tags = item.pop(u'tags', [])
-        return u", ".join(tags)
+        tags = item.pop('tags', [])
+        return ", ".join(tags)
 
     def format_updated(self, item, keys):
         return self.format_time(item["updated"])
@@ -273,11 +270,11 @@
 
     def format_url(self, item, keys):
         return uri.buildXMPPUri(
-            u"pubsub",
-            subtype=u"microblog",
-            path=self.metadata[u"service"],
-            node=self.metadata[u"node"],
-            item=item[u"id"],
+            "pubsub",
+            subtype="microblog",
+            path=self.metadata["service"],
+            node=self.metadata["node"],
+            item=item["id"],
         )
 
     def get_keys(self):
@@ -286,10 +283,10 @@
         if self.args.keys:
             if not set(MB_KEYS).issuperset(self.args.keys):
                 self.disp(
-                    u"following keys are invalid: {invalid}.\n"
-                    u"Valid keys are: {valid}.".format(
-                        invalid=u", ".join(set(self.args.keys).difference(MB_KEYS)),
-                        valid=u", ".join(sorted(MB_KEYS)),
+                    "following keys are invalid: {invalid}.\n"
+                    "Valid keys are: {valid}.".format(
+                        invalid=", ".join(set(self.args.keys).difference(MB_KEYS)),
+                        valid=", ".join(sorted(MB_KEYS)),
                     ),
                     error=True,
                 )
@@ -297,18 +294,18 @@
             return self.args.keys
         else:
             if verbosity == 0:
-                return (u"title", u"content")
+                return ("title", "content")
             elif verbosity == 1:
                 return (
-                    u"title",
-                    u"tags",
-                    u"author",
-                    u"author_jid",
-                    u"author_email",
-                    u"author_jid_verified",
-                    u"published",
-                    u"updated",
-                    u"content",
+                    "title",
+                    "tags",
+                    "author",
+                    "author_jid",
+                    "author_email",
+                    "author_jid_verified",
+                    "published",
+                    "updated",
+                    "content",
                 )
             else:
                 return MB_KEYS
@@ -334,19 +331,19 @@
                 if OUTPUT_OPT_NO_HEADER in self.args.output_opts:
                     header = ""
                 else:
-                    header = u"{k_fmt}{key}:{k_fmt_e} {sep}".format(
+                    header = "{k_fmt}{key}:{k_fmt_e} {sep}".format(
                         k_fmt=C.A_HEADER,
                         key=k,
                         k_fmt_e=A.RESET,
-                        sep=u"\n" if "content" in k else u"",
+                        sep="\n" if "content" in k else "",
                     )
                 value = k_cb[k](item, keys) if k in k_cb else item[k]
                 if isinstance(value, bool):
-                    value = unicode(value).lower()
+                    value = str(value).lower()
                 self.disp(header + value)
             # we want a separation line after each item but the last one
             if idx < len(items) - 1:
-                print(u"")
+                print("")
 
     def format_time(self, timestamp):
         """return formatted date for timestamp
@@ -354,7 +351,7 @@
         @param timestamp(str,int,float): unix timestamp
         @return (unicode): formatted date
         """
-        fmt = u"%d/%m/%Y %H:%M:%S"
+        fmt = "%d/%m/%Y %H:%M:%S"
         return time.strftime(fmt, time.localtime(float(timestamp)))
 
     def fancy_output(self, data):
@@ -363,46 +360,46 @@
         this output doesn't use keys filter
         """
         # thanks to http://stackoverflow.com/a/943921
-        rows, columns = map(int, os.popen("stty size", "r").read().split())
+        rows, columns = list(map(int, os.popen("stty size", "r").read().split()))
         items, metadata = data
         verbosity = self.args.verbose
-        sep = A.color(A.FG_BLUE, columns * u"▬")
+        sep = A.color(A.FG_BLUE, columns * "▬")
         if items:
-            print(u"\n" + sep + "\n")
+            print(("\n" + sep + "\n"))
 
         for idx, item in enumerate(items):
-            title = item.get(u"title")
+            title = item.get("title")
             if verbosity > 0:
-                author = item[u"author"]
-                published, updated = item[u"published"], item.get("updated")
+                author = item["author"]
+                published, updated = item["published"], item.get("updated")
             else:
                 author = published = updated = None
             if verbosity > 1:
                 tags = item.pop('tags', [])
             else:
                 tags = None
-            content = item.get(u"content")
+            content = item.get("content")
 
             if title:
-                print(A.color(A.BOLD, A.FG_CYAN, item[u"title"]))
+                print((A.color(A.BOLD, A.FG_CYAN, item["title"])))
             meta = []
             if author:
                 meta.append(A.color(A.FG_YELLOW, author))
             if published:
-                meta.append(A.color(A.FG_YELLOW, u"on ", self.format_time(published)))
+                meta.append(A.color(A.FG_YELLOW, "on ", self.format_time(published)))
             if updated != published:
                 meta.append(
-                    A.color(A.FG_YELLOW, u"(updated on ", self.format_time(updated), u")")
+                    A.color(A.FG_YELLOW, "(updated on ", self.format_time(updated), ")")
                 )
-            print(u" ".join(meta))
+            print((" ".join(meta)))
             if tags:
-                print(A.color(A.FG_MAGENTA, u", ".join(tags)))
+                print((A.color(A.FG_MAGENTA, ", ".join(tags))))
             if (title or tags) and content:
                 print("")
             if content:
                 self.disp(content)
 
-            print(u"\n" + sep + "\n")
+            print(("\n" + sep + "\n"))
 
     def mbGetCb(self, mb_result):
         items, metadata = mb_result
@@ -412,7 +409,7 @@
         self.host.quit(C.EXIT_OK)
 
     def mbGetEb(self, failure_):
-        self.disp(u"can't get blog items: {reason}".format(reason=failure_), error=True)
+        self.disp("can't get blog items: {reason}".format(reason=failure_), error=True)
         self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
     def start(self):
@@ -438,7 +435,7 @@
             pubsub_flags={C.SINGLE_ITEM},
             use_draft=True,
             use_verbose=True,
-            help=_(u"edit an existing or new blog post"),
+            help=_("edit an existing or new blog post"),
         )
         BlogPublishCommon.__init__(self)
         common.BaseEdit.__init__(self, self.host, BLOG_TMP_DIR, use_metadata=True)
@@ -449,7 +446,7 @@
             "-P",
             "--preview",
             action="store_true",
-            help=_(u"launch a blog preview in parallel"),
+            help=_("launch a blog preview in parallel"),
         )
 
     def buildMetadataFile(self, content_file_path, mb_data=None):
@@ -465,13 +462,13 @@
         # or re-use the existing one if it exists
         meta_file_path = os.path.splitext(content_file_path)[0] + common.METADATA_SUFF
         if os.path.exists(meta_file_path):
-            self.disp(u"Metadata file already exists, we re-use it")
+            self.disp("Metadata file already exists, we re-use it")
             try:
                 with open(meta_file_path, "rb") as f:
                     mb_data = json.load(f)
             except (OSError, IOError, ValueError) as e:
                 self.disp(
-                    u"Can't read existing metadata file at {path}, aborting: {reason}".format(
+                    "Can't read existing metadata file at {path}, aborting: {reason}".format(
                         path=meta_file_path, reason=e
                     ),
                     error=True,
@@ -513,7 +510,7 @@
 
         # do we need a preview ?
         if self.args.preview:
-            self.disp(u"Preview requested, launching it", 1)
+            self.disp("Preview requested, launching it", 1)
             # we redirect outputs to /dev/null to avoid console pollution in editor
             # if user wants to see messages, (s)he can call "blog preview" directly
             DEVNULL = open(os.devnull, "wb")
@@ -552,7 +549,7 @@
         self.host.bridge.mbSend(
             self.pubsub_service, self.pubsub_node, mb_data, self.profile
         )
-        self.disp(u"Blog item published")
+        self.disp("Blog item published")
 
     def getTmpSuff(self):
         # we get current syntax to determine file extension
@@ -577,15 +574,15 @@
         if content and self.current_syntax == SYNTAX_XHTML:
             content = content.strip()
             if not content.startswith('<div>'):
-                content = u'<div>' + content + u'</div>'
+                content = '<div>' + content + '</div>'
             try:
                 from lxml import etree
             except ImportError:
-                self.disp(_(u"You need lxml to edit pretty XHTML"))
+                self.disp(_("You need lxml to edit pretty XHTML"))
             else:
                 parser = etree.XMLParser(remove_blank_text=True)
                 root = etree.fromstring(content, parser)
-                content = etree.tostring(root, encoding=unicode, pretty_print=True)
+                content = etree.tostring(root, encoding=str, pretty_print=True)
 
         return content, mb_data, mb_data["id"]
 
@@ -599,11 +596,11 @@
                     self.current_syntax
                 )
             except Exception as e:
-                if "NotFound" in unicode(
+                if "NotFound" in str(
                     e
                 ):  #  FIXME: there is not good way to check bridge errors
                     self.parser.error(
-                        _(u"unknown syntax requested ({syntax})").format(
+                        _("unknown syntax requested ({syntax})").format(
                             syntax=self.args.syntax
                         )
                     )
@@ -627,7 +624,7 @@
 
     def __init__(self, host):
         base.CommandBase.__init__(
-            self, host, "preview", use_verbose=True, help=_(u"preview a blog content")
+            self, host, "preview", use_verbose=True, help=_("preview a blog content")
         )
         common.BaseEdit.__init__(self, self.host, BLOG_TMP_DIR, use_metadata=True)
 
@@ -636,15 +633,14 @@
             "--inotify",
             type=str,
             choices=("auto", "true", "false"),
-            default=u"auto",
-            help=_(u"use inotify to handle preview"),
+            default="auto",
+            help=_("use inotify to handle preview"),
         )
         self.parser.add_argument(
             "file",
-            type=base.unicode_decoder,
             nargs="?",
-            default=u"current",
-            help=_(u"path to the content file"),
+            default="current",
+            help=_("path to the content file"),
         )
 
     def showPreview(self):
@@ -660,7 +656,7 @@
         )
         if not args:
             self.disp(
-                u'Couln\'t find command in "{name}", abording'.format(name=opt_name),
+                'Couln\'t find command in "{name}", abording'.format(name=opt_name),
                 error=True,
             )
             self.host.quit(1)
@@ -683,11 +679,11 @@
                 )
 
         xhtml = (
-            u'<html xmlns="http://www.w3.org/1999/xhtml">'
-            u'<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />'
-            u"</head>"
-            u"<body>{}</body>"
-            u"</html>"
+            '<html xmlns="http://www.w3.org/1999/xhtml">'
+            '<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />'
+            "</head>"
+            "<body>{}</body>"
+            "</html>"
         ).format(content)
 
         with open(self.preview_file_path, "wb") as f:
@@ -695,7 +691,7 @@
 
     def start(self):
         import webbrowser
-        import urllib
+        import urllib.request, urllib.parse, urllib.error
 
         self.webbrowser, self.urllib = webbrowser, urllib
 
@@ -708,13 +704,13 @@
                 if self.args.inotify == "auto":
                     inotify = None
                     self.disp(
-                        u"inotify module not found, deactivating feature. You can install"
-                        u" it with {install}".format(install=INOTIFY_INSTALL)
+                        "inotify module not found, deactivating feature. You can install"
+                        " it with {install}".format(install=INOTIFY_INSTALL)
                     )
                 else:
                     self.disp(
-                        u"inotify not found, can't activate the feature! Please install "
-                        u"it with {install}".format(install=INOTIFY_INSTALL),
+                        "inotify not found, can't activate the feature! Please install "
+                        "it with {install}".format(install=INOTIFY_INSTALL),
                         error=True,
                     )
                     self.host.quit(1)
@@ -724,7 +720,7 @@
                     inotify.adapters._LOGGER.setLevel(40)
                 except AttributeError:
                     self.disp(
-                        u"Logger doesn't exists, inotify may have chanded", error=True
+                        "Logger doesn't exists, inotify may have chanded", error=True
                     )
         else:
             inotify = None
@@ -768,8 +764,8 @@
             # XXX: we don't delete file automatically because browser need it
             #      (and webbrowser.open can return before it is read)
             self.disp(
-                u"temporary file created at {}\nthis file will NOT BE DELETED "
-                u"AUTOMATICALLY, please delete it yourself when you have finished".format(
+                "temporary file created at {}\nthis file will NOT BE DELETED "
+                "AUTOMATICALLY, please delete it yourself when you have finished".format(
                     self.preview_file_path
                 )
             )
@@ -793,10 +789,10 @@
             try:
                 for event in i.event_gen():
                     if event is not None:
-                        self.disp(u"Content updated", 1)
+                        self.disp("Content updated", 1)
                         if {"IN_DELETE_SELF", "IN_MOVE_SELF"}.intersection(event[1]):
                             self.disp(
-                                u"{} event catched, changing the watch".format(
+                                "{} event catched, changing the watch".format(
                                     ", ".join(event[1])
                                 ),
                                 2,
@@ -813,7 +809,7 @@
                         update_cb()
             except InotifyError:
                 self.disp(
-                    u"Can't catch inotify events, as the file been deleted?", error=True
+                    "Can't catch inotify events, as the file been deleted?", error=True
                 )
             finally:
                 os.unlink(self.preview_file_path)
@@ -830,28 +826,27 @@
             "import",
             use_pubsub=True,
             use_progress=True,
-            help=_(u"import an external blog"),
+            help=_("import an external blog"),
         )
         self.need_loop = True
 
     def add_parser_options(self):
         self.parser.add_argument(
             "importer",
-            type=base.unicode_decoder,
             nargs="?",
-            help=_(u"importer name, nothing to display importers list"),
+            help=_("importer name, nothing to display importers list"),
         )
         self.parser.add_argument(
-            "--host", type=base.unicode_decoder, help=_(u"original blog host")
+            "--host", help=_("original blog host")
         )
         self.parser.add_argument(
             "--no-images-upload",
             action="store_true",
-            help=_(u"do *NOT* upload images (default: do upload images)"),
+            help=_("do *NOT* upload images (default: do upload images)"),
         )
         self.parser.add_argument(
             "--upload-ignore-host",
-            help=_(u"do not upload images from this host (default: upload all images)"),
+            help=_("do not upload images from this host (default: upload all images)"),
         )
         self.parser.add_argument(
             "--ignore-tls-errors",
@@ -864,52 +859,51 @@
             action="append",
             nargs=2,
             default=[],
-            metavar=(u"NAME", u"VALUE"),
-            help=_(u"importer specific options (see importer description)"),
+            metavar=("NAME", "VALUE"),
+            help=_("importer specific options (see importer description)"),
         )
         self.parser.add_argument(
             "location",
-            type=base.unicode_decoder,
             nargs="?",
             help=_(
-                u"importer data location (see importer description), nothing to show "
-                u"importer description"
+                "importer data location (see importer description), nothing to show "
+                "importer description"
             ),
         )
 
     def onProgressStarted(self, metadata):
-        self.disp(_(u"Blog upload started"), 2)
+        self.disp(_("Blog upload started"), 2)
 
     def onProgressFinished(self, metadata):
-        self.disp(_(u"Blog uploaded successfully"), 2)
+        self.disp(_("Blog uploaded successfully"), 2)
         redirections = {
             k[len(URL_REDIRECT_PREFIX) :]: v
-            for k, v in metadata.iteritems()
+            for k, v in metadata.items()
             if k.startswith(URL_REDIRECT_PREFIX)
         }
         if redirections:
-            conf = u"\n".join(
+            conf = "\n".join(
                 [
-                    u"url_redirections_dict = {}".format(
+                    "url_redirections_dict = {}".format(
                         # we need to add ' ' before each new line
                         # and to double each '%' for ConfigParser
-                        u"\n ".join(
+                        "\n ".join(
                             json.dumps(redirections, indent=1, separators=(",", ": "))
-                            .replace(u"%", u"%%")
-                            .split(u"\n")
+                            .replace("%", "%%")
+                            .split("\n")
                         )
                     ),
                 ]
             )
             self.disp(
                 _(
-                    u"\nTo redirect old URLs to new ones, put the following lines in your"
-                    u" sat.conf file, in [libervia] section:\n\n{conf}".format(conf=conf)
+                    "\nTo redirect old URLs to new ones, put the following lines in your"
+                    " sat.conf file, in [libervia] section:\n\n{conf}".format(conf=conf)
                 )
             )
 
     def onProgressError(self, error_msg):
-        self.disp(_(u"Error while uploading blog: {}").format(error_msg), error=True)
+        self.disp(_("Error while uploading blog: {}").format(error_msg), error=True)
 
     def error(self, failure):
         self.disp(
@@ -924,14 +918,14 @@
                 if getattr(self.args, name):
                     self.parser.error(
                         _(
-                            u"{name} argument can't be used without location argument"
+                            "{name} argument can't be used without location argument"
                         ).format(name=name)
                     )
             if self.args.importer is None:
                 self.disp(
-                    u"\n".join(
+                    "\n".join(
                         [
-                            u"{}: {}".format(name, desc)
+                            "{}: {}".format(name, desc)
                             for name, desc in self.host.bridge.blogImportList()
                         ]
                     )
@@ -942,14 +936,14 @@
                         self.args.importer
                     )
                 except Exception as e:
-                    msg = [l for l in unicode(e).split("\n") if l][
+                    msg = [l for l in str(e).split("\n") if l][
                         -1
                     ]  # we only keep the last line
                     self.disp(msg)
                     self.host.quit(1)
                 else:
                     self.disp(
-                        u"{name}: {short_desc}\n\n{long_desc}".format(
+                        "{name}: {short_desc}\n\n{long_desc}".format(
                             name=self.args.importer,
                             short_desc=short_desc,
                             long_desc=long_desc,
@@ -967,8 +961,8 @@
                 options["upload_images"] = C.BOOL_FALSE
                 if self.args.upload_ignore_host:
                     self.parser.error(
-                        u"upload-ignore-host option can't be used when no-images-upload "
-                        u"is set"
+                        "upload-ignore-host option can't be used when no-images-upload "
+                        "is set"
                     )
             elif self.args.upload_ignore_host:
                 options["upload_ignore_host"] = self.args.upload_ignore_host