changeset 3568:04283582966f

core, frontends: fix invalid translatable strings. Some f-strings where used in translatable text, this has been fixed by using explicit `format()` call (using a script based on `tokenize`). As tokenize messes with spaces, a reformating tool (`black`) has been applied to some files afterwards.
author Goffi <goffi@goffi.org>
date Mon, 14 Jun 2021 18:35:12 +0200
parents a240748ed686
children 828fc830633a
files sat/memory/params.py sat/plugins/plugin_import.py sat/plugins/plugin_xep_0329.py sat/tools/common/async_process.py sat_frontends/jp/base.py sat_frontends/jp/cmd_account.py sat_frontends/jp/cmd_adhoc.py sat_frontends/jp/cmd_avatar.py sat_frontends/jp/cmd_blog.py sat_frontends/jp/cmd_bookmarks.py sat_frontends/jp/cmd_debug.py sat_frontends/jp/cmd_event.py sat_frontends/jp/cmd_file.py sat_frontends/jp/cmd_info.py sat_frontends/jp/cmd_invitation.py sat_frontends/jp/cmd_list.py sat_frontends/jp/cmd_merge_request.py sat_frontends/jp/cmd_param.py sat_frontends/jp/cmd_ping.py sat_frontends/jp/cmd_pubsub.py sat_frontends/jp/common.py sat_frontends/jp/xmlui_manager.py
diffstat 22 files changed, 821 insertions(+), 599 deletions(-) [+]
line wrap: on
line diff
--- a/sat/memory/params.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat/memory/params.py	Mon Jun 14 18:35:12 2021 +0200
@@ -972,8 +972,12 @@
 
         if not self.checkSecurityLimit(node[1], security_limit):
             msg = _(
-                f"{profile!r} is trying to set parameter {name!r} in category "
-                f"{category!r} without authorization!!!")
+                "{profile!r} is trying to set parameter {name!r} in category "
+                "{category!r} without authorization!!!").format(
+                    profile=repr(profile),
+                    name=repr(name),
+                    category=repr(category)
+                )
             log.warning(msg)
             raise exceptions.PermissionError(msg)
 
@@ -986,8 +990,11 @@
                     int(value)
                 except ValueError:
                     log.warning(_(
-                        f"Trying to set parameter {name!r} in category {category!r} with"
-                        f"an non-integer value"
+                        "Trying to set parameter {name} in category {category} with"
+                        "an non-integer value"
+                    ).format(
+                        name=repr(name),
+                        category=repr(category)
                     ))
                     return defer.succeed(None)
                 if node[1].hasAttribute("constraint"):
--- a/sat/plugins/plugin_import.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat/plugins/plugin_import.py	Mon Jun 14 18:35:12 2021 +0200
@@ -64,7 +64,7 @@
         @param name(unicode): import handler name
         """
         assert name == name.lower().strip()
-        log.info(_(f"initializing {name} import handler"))
+        log.info(_("initializing {name} import handler").format(name=name))
         import_handler.name = name
         import_handler.register = partial(self.register, import_handler)
         import_handler.unregister = partial(self.unregister, import_handler)
@@ -155,7 +155,7 @@
                 pass
             except ValueError:
                 raise exceptions.DataError(
-                    _(f"invalid json option: {option}")
+                    _("invalid json option: {option}").format(option=option)
                 )
         pubsub_service = jid.JID(pubsub_service) if pubsub_service else None
         return self.doImport(
--- a/sat/plugins/plugin_xep_0329.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat/plugins/plugin_xep_0329.py	Mon Jun 14 18:35:12 2021 +0200
@@ -708,7 +708,8 @@
                 )
             else:
                 log.warning(
-                    _(f"unexpected element, ignoring: {elt.toXml()}")
+                    _("unexpected element, ignoring: {elt}")
+                    .format(elt=elt.toXml())
                 )
                 continue
             files.append(file_data)
--- a/sat/tools/common/async_process.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat/tools/common/async_process.py	Mon Jun 14 18:35:12 2021 +0200
@@ -76,7 +76,7 @@
     def processEnded(self, reason):
         data = b''.join(self.data)
         if (reason.value.exitCode == 0):
-            log.debug(_(f'{self.command_name!r} command succeed'))
+            log.debug(f'{self.command_name!r} command succeed')
             # we don't use "replace" on purpose, we want an exception if decoding
             # is not working properly
             self._deferred.callback(data)
--- a/sat_frontends/jp/base.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/base.py	Mon Jun 14 18:35:12 2021 +0200
@@ -153,8 +153,10 @@
             background = self.guess_background()
         if background not in ('dark', 'light'):
             raise exceptions.ConfigError(_(
-                f'Invalid value set for "background" ({background!r}), please check '
-                f'your settings in libervia.conf'))
+                'Invalid value set for "background" ({background}), please check '
+                'your settings in libervia.conf').format(
+                    background=repr(background)
+                ))
         if background == 'light':
             C.A_HEADER = A.FG_MAGENTA
             C.A_SUBHEADER = A.BOLD + A.FG_RED
@@ -449,7 +451,8 @@
         if default:
             if type_ in self.default_output:
                 self.disp(
-                    _(f'there is already a default output for {type_}, ignoring new one')
+                    _('there is already a default output for {type}, ignoring new one')
+                    .format(type=type_)
                 )
             else:
                 self.default_output[type_] = name
@@ -493,7 +496,8 @@
                     self.import_plugin_module(module, type_)
                 except ImportError as e:
                     self.disp(
-                        _(f"Can't import {module_path} plugin, ignoring it: {e}"),
+                        _("Can't import {module_path} plugin, ignoring it: {e}")
+                        .format(module_path=module_path, e=e),
                         error=True)
                 except exceptions.CancelError:
                     continue
@@ -512,7 +516,10 @@
         try:
             class_names =  getattr(module, '__{}__'.format(type_))
         except AttributeError:
-            log.disp(_(f"Invalid plugin module [{type_}] {module}"), error=True)
+            log.disp(
+                _("Invalid plugin module [{type}] {module}")
+                .format(type=type_, module=module),
+                error=True)
             raise ImportError
         else:
             for class_name in class_names:
@@ -601,7 +608,9 @@
                                 if not item_last:
                                     self.args.item = uri_item
                 else:
-                    self.parser.error(_(f'XMPP URL is not a pubsub one: {url}'))
+                    self.parser.error(
+                        _('XMPP URL is not a pubsub one: {url}').format(url=url)
+                    )
         flags = self.args._cmd._pubsub_flags
         # we check required arguments here instead of using add_arguments' required option
         # because the required argument can be set in URL
@@ -647,13 +656,15 @@
             await self.bridge.bridgeConnect()
         except Exception as e:
             if isinstance(e, exceptions.BridgeExceptionNoService):
-                print((_("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 ?"))
                 self.quit(C.EXIT_BACKEND_NOT_FOUND, raise_exc=False)
             elif isinstance(e, exceptions.BridgeInitError):
-                print((_("Can't init bridge")))
+                print(_("Can't init bridge"))
                 self.quit(C.EXIT_BRIDGE_ERROR, raise_exc=False)
             else:
-                print((_(f"Error while initialising bridge: {e}")))
+                print(
+                    _("Error while initialising bridge: {e}").format(e=e)
+                )
                 self.quit(C.EXIT_BRIDGE_ERROR, raise_exc=False)
             return
         self.version = await self.bridge.getVersion()
@@ -844,7 +855,10 @@
         self.profile = await self.bridge.profileNameGet(self.args.profile)
 
         if not self.profile:
-            log.error(_(f"The profile [{self.args.profile}] doesn't exist"))
+            log.error(
+                _("The profile [{profile}] doesn't exist")
+                .format(profile=self.args.profile)
+            )
             self.quit(C.EXIT_ERROR)
 
         try:
@@ -861,9 +875,11 @@
             elif not await self.bridge.profileIsSessionStarted(self.profile):
                 if not self.args.connect:
                     self.disp(_(
-                        f"Session for [{self.profile}] is not started, please start it "
-                        f"before using jp, or use either --start-session or --connect "
-                        f"option"), error=True)
+                        "Session for [{profile}] is not started, please start it "
+                        "before using jp, or use either --start-session or --connect "
+                        "option"
+                        .format(profile=self.profile)
+                    ), error=True)
                     self.quit(1)
             elif not getattr(self.args, "connect", False):
                 return
@@ -882,8 +898,10 @@
         else:
             if not await self.bridge.isConnected(self.profile):
                 log.error(
-                    _(f"Profile [{self.profile}] is not connected, please connect it "
-                      f"before using jp, or use --connect option"))
+                    _("Profile [{profile}] is not connected, please connect it "
+                      "before using jp, or use --connect option")
+                    .format(profile=self.profile)
+                )
                 self.quit(1)
 
     async def get_full_jid(self, param_jid):
@@ -1158,7 +1176,7 @@
 
         @param error_msg(unicode): error message as sent by bridge.progressError
         """
-        self.disp(_(f"Error while doing operation: {e}"), error=True)
+        self.disp(_("Error while doing operation: {e}").format(e=e), error=True)
 
     def disp(self, msg, verbosity=0, error=False, end='\n'):
         return self.host.disp(msg, verbosity, error, end)
--- a/sat_frontends/jp/cmd_account.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_account.py	Mon Jun 14 18:35:12 2021 +0200
@@ -73,7 +73,9 @@
             "--port",
             type=int,
             default=0,
-            help=_(f"server port (default: {C.XMPP_C2S_PORT})"),
+            help=_("server port (default: {port})").format(
+                port=C.XMPP_C2S_PORT
+            ),
         )
 
     async def start(self):
@@ -124,8 +126,12 @@
                 self.host.quit(C.EXIT_CONFLICT)
             else:
                 self.disp(
-                    _(f"Can't create profile {self.args.profile} to associate with jid "
-                      f"{self.args.jid}: {e}"),
+                    _("Can't create profile {profile} to associate with jid "
+                      "{jid}: {e}").format(
+                          profile=self.args.profile,
+                          jid=self.args.jid,
+                          e=e
+                      ),
                     error=True,
                 )
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
--- a/sat_frontends/jp/cmd_adhoc.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_adhoc.py	Mon Jun 14 18:35:12 2021 +0200
@@ -97,7 +97,8 @@
                 for method in methods:
                     path, iface, command = method
                     self.disp(
-                        _(f"Command found: (path:{path}, iface: {iface}) [{command}]"),
+                        _("Command found: (path:{path}, iface: {iface}) [{command}]")
+                        .format(path=path, iface=iface, command=command),
                         1,
                     )
                 self.host.quit()
--- a/sat_frontends/jp/cmd_avatar.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_avatar.py	Mon Jun 14 18:35:12 2021 +0200
@@ -113,7 +113,7 @@
     async def start(self):
         path = self.args.image_path
         if not os.path.exists(path):
-            self.disp(_(f"file {path!r} doesn't exist!"), error=True)
+            self.disp(_("file {path} doesn't exist!").format(path=repr(path)), error=True)
             self.host.quit(C.EXIT_BAD_ARG)
         path = os.path.abspath(path)
         try:
--- a/sat_frontends/jp/cmd_blog.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_blog.py	Mon Jun 14 18:35:12 2021 +0200
@@ -100,7 +100,7 @@
             if k and ext == v:
                 return k
 
-    # if not found, we use current syntax
+                # if not found, we use current syntax
     return await host.bridge.getParamA("Syntax", "Composition", "value", host.profile)
 
 
@@ -125,15 +125,17 @@
                 self.current_syntax = self.args.syntax = syntax
             except Exception as e:
                 if e.classname == "NotFound":
-                    self.parser.error(_(f"unknown syntax requested ({self.args.syntax})"))
+                    self.parser.error(
+                        _("unknown syntax requested ({syntax})").format(
+                            syntax=self.args.syntax
+                        )
+                    )
                 else:
                     raise e
         return self.args.syntax
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "-T", "--title", help=_("title of the item")
-        )
+        self.parser.add_argument("-T", "--title", help=_("title of the item"))
         self.parser.add_argument(
             "-t",
             "--tag",
@@ -148,13 +150,22 @@
 
         comments_group = self.parser.add_mutually_exclusive_group()
         comments_group.add_argument(
-            "-C", "--comments", action="store_const", const=True, dest="comments",
-            help=_("enable comments (default: comments not enabled except if they "
-                   "already exist)")
+            "-C",
+            "--comments",
+            action="store_const",
+            const=True,
+            dest="comments",
+            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=_("disable comments (will remove comments node if it exist)")
+            "--no-comments",
+            action="store_const",
+            const=False,
+            dest="comments",
+            help=_("disable comments (will remove comments node if it exist)"),
         )
 
         self.parser.add_argument(
@@ -182,7 +193,7 @@
         if self.args.comments is not None:
             mb_data["allow_comments"] = self.args.comments
         if self.args.tag:
-            mb_data['tags'] = self.args.tag
+            mb_data["tags"] = self.args.tag
         if self.args.title is not None:
             mb_data["title"] = self.args.title
         if self.args.language is not None:
@@ -222,9 +233,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(
-                f"can't send item: {e}", error=True
-            )
+            self.disp(f"can't send item: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp("Item published")
@@ -261,12 +270,12 @@
 
     def template_data_mapping(self, data):
         items, blog_items = data
-        blog_items['items'] = items
+        blog_items["items"] = items
         return {"blog_items": blog_items}
 
     def format_comments(self, item, keys):
         lines = []
-        for data in item.get('comments', []):
+        for data in item.get("comments", []):
             lines.append(data["uri"])
             for k in ("node", "service"):
                 if OUTPUT_OPT_NO_HEADER in self.args.output_opts:
@@ -277,7 +286,7 @@
         return "\n".join(lines)
 
     def format_tags(self, item, keys):
-        tags = item.pop('tags', [])
+        tags = item.pop("tags", [])
         return ", ".join(tags)
 
     def format_updated(self, item, keys):
@@ -359,7 +368,7 @@
                 if isinstance(value, bool):
                     value = str(value).lower()
                 self.disp(header + value)
-            # we want a separation line after each item but the last one
+                # we want a separation line after each item but the last one
             if idx < len(items) - 1:
                 print("")
 
@@ -393,7 +402,7 @@
             else:
                 author = published = updated = None
             if verbosity > 1:
-                tags = item.pop('tags', [])
+                tags = item.pop("tags", [])
             else:
                 tags = None
             content = item.get("content")
@@ -428,14 +437,14 @@
                     self.args.max,
                     self.args.items,
                     self.getPubsubExtra(),
-                    self.profile
+                    self.profile,
                 )
             )
         except Exception as e:
             self.disp(f"can't get blog items: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            items = mb_data.pop('items')
+            items = mb_data.pop("items")
             await self.output((items, mb_data))
             self.host.quit(C.EXIT_OK)
 
@@ -483,7 +492,8 @@
         # we first construct metadata from edited item ones and CLI argumments
         # or re-use the existing one if it exists
         meta_file_path = content_file_path.with_name(
-            content_file_path.stem + common.METADATA_SUFF)
+            content_file_path.stem + common.METADATA_SUFF
+        )
         if meta_file_path.exists():
             self.disp("Metadata file already exists, we re-use it")
             try:
@@ -499,20 +509,20 @@
         else:
             mb_data = {} if mb_data is None else mb_data.copy()
 
-        # in all cases, we want to remove unwanted keys
+            # in all cases, we want to remove unwanted keys
         for key in KEY_TO_REMOVE_METADATA:
             try:
                 del mb_data[key]
             except KeyError:
                 pass
-        # and override metadata with command-line arguments
+                # and override metadata with command-line arguments
         self.setMbDataFromArgs(mb_data)
 
         if self.args.no_publish:
             mb_data["publish"] = False
 
-        # then we create the file and write metadata there, as JSON dict
-        # XXX: if we port jp one day on Windows, O_BINARY may need to be added here
+            # then we create the file and write metadata there, as JSON dict
+            # XXX: if we port jp one day on Windows, O_BINARY may need to be added here
         with os.fdopen(
             os.open(meta_file_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o600), "w+b"
         ) as f:
@@ -556,7 +566,7 @@
                 )
             )
 
-        # we launch editor
+            # we launch editor
         coroutines.append(
             self.runEditor(
                 "blog_editor_args",
@@ -590,11 +600,9 @@
         items = [item] if item else []
 
         mb_data = data_format.deserialise(
-            await self.host.bridge.mbGet(
-                service, node, 1, items, {}, self.profile
-            )
+            await self.host.bridge.mbGet(service, node, 1, items, {}, self.profile)
         )
-        item = mb_data['items'][0]
+        item = mb_data["items"][0]
 
         try:
             content = item["content_xhtml"]
@@ -612,8 +620,8 @@
 
         if content and self.current_syntax == SYNTAX_XHTML:
             content = content.strip()
-            if not content.startswith('<div>'):
-                content = '<div>' + content + '</div>'
+            if not content.startswith("<div>"):
+                content = "<div>" + content + "</div>"
             try:
                 from lxml import etree
             except ImportError:
@@ -630,15 +638,20 @@
         SYNTAX_EXT.update(config.getConfig(self.sat_conf, "jp", CONF_SYNTAX_EXT, {}))
         self.current_syntax = await self.get_current_syntax()
 
-        (self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path,
-         content_file_obj, mb_data,) = await self.getItemPath()
+        (
+            self.pubsub_service,
+            self.pubsub_node,
+            self.pubsub_item,
+            content_file_path,
+            content_file_obj,
+            mb_data,
+        ) = await self.getItemPath()
 
         await self.edit(content_file_path, content_file_obj, mb_data=mb_data)
         self.host.quit()
 
 
 class Rename(base.CommandBase):
-
     def __init__(self, host):
         base.CommandBase.__init__(
             self,
@@ -650,10 +663,7 @@
         )
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "new_id",
-            help=_("new item id to use")
-        )
+        self.parser.add_argument("new_id", help=_("new item id to use"))
 
     async def start(self):
         try:
@@ -665,14 +675,13 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(
-                f"can't rename item: {e}", error=True
-            )
+            self.disp(f"can't rename item: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp("Item renamed")
             self.host.quit(C.EXIT_OK)
 
+
 class Preview(base.CommandBase, common.BaseEdit):
     # TODO: need to be rewritten with template output
 
@@ -735,9 +744,9 @@
         xhtml = (
             f'<html xmlns="http://www.w3.org/1999/xhtml">'
             f'<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />'
-            f'</head>'
-            f'<body>{content}</body>'
-            f'</html>'
+            f"</head>"
+            f"<body>{content}</body>"
+            f"</html>"
         )
 
         with open(self.preview_file_path, "wb") as f:
@@ -791,18 +800,19 @@
         else:
             update_cb = self.updatePreviewExt
 
-        # which file do we need to edit?
+            # which file do we need to edit?
         if self.args.file == "current":
             self.content_file_path = self.getCurrentFile(self.profile)
         else:
             try:
                 self.content_file_path = Path(self.args.file).resolve(strict=True)
             except FileNotFoundError:
-                self.disp(_(f'File "{self.args.file}" doesn\'t exist!'))
+                self.disp(_('File "{file}" doesn\'t exist!').format(file=self.args.file))
                 self.host.quit(C.EXIT_NOT_FOUND)
 
         self.syntax = await guessSyntaxFromPath(
-            self.host, sat_conf, self.content_file_path)
+            self.host, sat_conf, self.content_file_path
+        )
 
         # at this point the syntax is converted, we can display the preview
         preview_file = tempfile.NamedTemporaryFile(suffix=".xhtml", delete=False)
@@ -825,10 +835,10 @@
             watcher_kwargs = {
                 # Watcher don't accept Path so we convert to string
                 "path": str(self.content_file_path),
-                "alias": 'content_file',
+                "alias": "content_file",
                 "flags": aionotify.Flags.CLOSE_WRITE
-                         | aionotify.Flags.DELETE_SELF
-                         | aionotify.Flags.MOVE_SELF,
+                | aionotify.Flags.DELETE_SELF
+                | aionotify.Flags.MOVE_SELF,
             }
             watcher.watch(**watcher_kwargs)
 
@@ -839,14 +849,15 @@
                 while True:
                     event = await watcher.get_event()
                     self.disp("Content updated", 1)
-                    if event.flags & (aionotify.Flags.DELETE_SELF
-                                      | aionotify.Flags.MOVE_SELF):
+                    if event.flags & (
+                        aionotify.Flags.DELETE_SELF | aionotify.Flags.MOVE_SELF
+                    ):
                         self.disp(
                             "DELETE/MOVE event catched, changing the watch",
                             2,
                         )
                         try:
-                            watcher.unwatch('content_file')
+                            watcher.unwatch("content_file")
                         except IOError as e:
                             self.disp(
                                 f"Can't remove the watch: {e}",
@@ -869,7 +880,7 @@
             finally:
                 os.unlink(self.preview_file_path)
                 try:
-                    watcher.unwatch('content_file')
+                    watcher.unwatch("content_file")
                 except IOError as e:
                     self.disp(
                         f"Can't remove the watch: {e}",
@@ -893,9 +904,7 @@
             nargs="?",
             help=_("importer name, nothing to display importers list"),
         )
-        self.parser.add_argument(
-            "--host", help=_("original blog host")
-        )
+        self.parser.add_argument("--host", help=_("original blog host"))
         self.parser.add_argument(
             "--no-images-upload",
             action="store_true",
@@ -954,13 +963,16 @@
             )
             self.disp(
                 _(
-                    f"\nTo redirect old URLs to new ones, put the following lines in your"
-                    f" sat.conf file, in [libervia] section:\n\n{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)
             )
 
     async def onProgressError(self, error_msg):
-        self.disp(_(f"Error while uploading blog: {error_msg}"), error=True)
+        self.disp(
+            _("Error while uploading blog: {error_msg}").format(error_msg=error_msg),
+            error=True,
+        )
 
     async def start(self):
         if self.args.location is None:
@@ -968,8 +980,8 @@
                 if getattr(self.args, name):
                     self.parser.error(
                         _(
-                            f"{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(
@@ -1022,13 +1034,14 @@
                 )
             except Exception as e:
                 self.disp(
-                    _(f"Error while trying to import a blog: {e}"),
+                    _("Error while trying to import a blog: {e}").format(e=e),
                     error=True,
                 )
                 self.host.quit(1)
 
             await self.set_progress_id(progress_id)
 
+
 class Blog(base.CommandBase):
     subcommands = (Set, Get, Edit, Rename, Preview, Import)
 
--- a/sat_frontends/jp/cmd_bookmarks.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_bookmarks.py	Mon Jun 14 18:35:12 2021 +0200
@@ -23,25 +23,41 @@
 
 __commands__ = ["Bookmarks"]
 
-STORAGE_LOCATIONS = ('local', 'private', 'pubsub')
-TYPES = ('muc', 'url')
+STORAGE_LOCATIONS = ("local", "private", "pubsub")
+TYPES = ("muc", "url")
+
 
 class BookmarksCommon(base.CommandBase):
     """Class used to group common options of bookmarks subcommands"""
 
-    def add_parser_options(self, location_default='all'):
-        self.parser.add_argument('-l', '--location', type=str, choices=(location_default,) + STORAGE_LOCATIONS, default=location_default, help=_("storage location (default: %(default)s)"))
-        self.parser.add_argument('-t', '--type', type=str, choices=TYPES, default=TYPES[0], help=_("bookmarks type (default: %(default)s)"))
+    def add_parser_options(self, location_default="all"):
+        self.parser.add_argument(
+            "-l",
+            "--location",
+            type=str,
+            choices=(location_default,) + STORAGE_LOCATIONS,
+            default=location_default,
+            help=_("storage location (default: %(default)s)"),
+        )
+        self.parser.add_argument(
+            "-t",
+            "--type",
+            type=str,
+            choices=TYPES,
+            default=TYPES[0],
+            help=_("bookmarks type (default: %(default)s)"),
+        )
+
 
 class BookmarksList(BookmarksCommon):
-
     def __init__(self, host):
-        super(BookmarksList, self).__init__(host, 'list', help=_('list bookmarks'))
+        super(BookmarksList, self).__init__(host, "list", help=_("list bookmarks"))
 
     async def start(self):
         try:
             data = await self.host.bridge.bookmarksList(
-                self.args.type, self.args.location, self.host.profile)
+                self.args.type, self.args.location, self.host.profile
+            )
         except Exception as e:
             self.disp(f"can't get bookmarks list: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
@@ -54,32 +70,40 @@
             loc_mess.append(f"{location}:")
             book_mess = []
             for book_link, book_data in list(data[location].items()):
-                name = book_data.get('name')
-                autojoin = book_data.get('autojoin', 'false') == 'true'
-                nick = book_data.get('nick')
-                book_mess.append("\t%s[%s%s]%s" % ((name+' ') if name else '',
-                                                 book_link,
-                                                 ' (%s)' % nick if nick else '',
-                                                 ' (*)' if autojoin else ''))
-            loc_mess.append('\n'.join(book_mess))
-            mess.append('\n'.join(loc_mess))
+                name = book_data.get("name")
+                autojoin = book_data.get("autojoin", "false") == "true"
+                nick = book_data.get("nick")
+                book_mess.append(
+                    "\t%s[%s%s]%s"
+                    % (
+                        (name + " ") if name else "",
+                        book_link,
+                        " (%s)" % nick if nick else "",
+                        " (*)" if autojoin else "",
+                    )
+                )
+            loc_mess.append("\n".join(book_mess))
+            mess.append("\n".join(loc_mess))
 
-        print('\n\n'.join(mess))
+        print("\n\n".join(mess))
         self.host.quit()
 
 
 class BookmarksRemove(BookmarksCommon):
-
     def __init__(self, host):
-        super(BookmarksRemove, self).__init__(host, 'remove', help=_('remove a bookmark'))
+        super(BookmarksRemove, self).__init__(host, "remove", help=_("remove a bookmark"))
 
     def add_parser_options(self):
         super(BookmarksRemove, self).add_parser_options()
         self.parser.add_argument(
-            'bookmark', help=_('jid (for muc bookmark) or url of to remove'))
+            "bookmark", help=_("jid (for muc bookmark) or url of to remove")
+        )
         self.parser.add_argument(
-            "-f", "--force", action="store_true",
-            help=_("delete bookmark without confirmation"),)
+            "-f",
+            "--force",
+            action="store_true",
+            help=_("delete bookmark without confirmation"),
+        )
 
     async def start(self):
         if not self.args.force:
@@ -87,50 +111,58 @@
 
         try:
             await self.host.bridge.bookmarksRemove(
-                self.args.type, self.args.bookmark, self.args.location, self.host.profile)
+                self.args.type, self.args.bookmark, self.args.location, self.host.profile
+            )
         except Exception as e:
-            self.disp(_(f"can't delete bookmark: {e}"), error=True)
+            self.disp(_("can't delete bookmark: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_('bookmark deleted'))
+            self.disp(_("bookmark deleted"))
             self.host.quit()
 
 
 class BookmarksAdd(BookmarksCommon):
-
     def __init__(self, host):
-        super(BookmarksAdd, self).__init__(host, 'add', help=_('add a bookmark'))
+        super(BookmarksAdd, self).__init__(host, "add", help=_("add a bookmark"))
 
     def add_parser_options(self):
-        super(BookmarksAdd, self).add_parser_options(location_default='auto')
+        super(BookmarksAdd, self).add_parser_options(location_default="auto")
         self.parser.add_argument(
-            'bookmark', help=_('jid (for muc bookmark) or url of to remove'))
-        self.parser.add_argument('-n', '--name', help=_("bookmark name"))
-        muc_group = self.parser.add_argument_group(_('MUC specific options'))
-        muc_group.add_argument('-N', '--nick', help=_('nickname'))
+            "bookmark", help=_("jid (for muc bookmark) or url of to remove")
+        )
+        self.parser.add_argument("-n", "--name", help=_("bookmark name"))
+        muc_group = self.parser.add_argument_group(_("MUC specific options"))
+        muc_group.add_argument("-N", "--nick", help=_("nickname"))
         muc_group.add_argument(
-            '-a', '--autojoin', action='store_true',
-            help=_('join room on profile connection'))
+            "-a",
+            "--autojoin",
+            action="store_true",
+            help=_("join room on profile connection"),
+        )
 
     async def start(self):
-        if self.args.type == 'url' and (self.args.autojoin or self.args.nick is not None):
+        if self.args.type == "url" and (self.args.autojoin or self.args.nick is not None):
             self.parser.error(_("You can't use --autojoin or --nick with --type url"))
         data = {}
         if self.args.autojoin:
-            data['autojoin'] = 'true'
+            data["autojoin"] = "true"
         if self.args.nick is not None:
-            data['nick'] = self.args.nick
+            data["nick"] = self.args.nick
         if self.args.name is not None:
-            data['name'] = self.args.name
+            data["name"] = self.args.name
         try:
             await self.host.bridge.bookmarksAdd(
-                self.args.type, self.args.bookmark, data, self.args.location,
-                self.host.profile)
+                self.args.type,
+                self.args.bookmark,
+                data,
+                self.args.location,
+                self.host.profile,
+            )
         except Exception as e:
             self.disp(f"can't add bookmark: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_('bookmark successfully added'))
+            self.disp(_("bookmark successfully added"))
             self.host.quit()
 
 
@@ -138,4 +170,6 @@
     subcommands = (BookmarksList, BookmarksRemove, BookmarksAdd)
 
     def __init__(self, host):
-        super(Bookmarks, self).__init__(host, 'bookmarks', use_profile=False, help=_('manage bookmarks'))
+        super(Bookmarks, self).__init__(
+            host, "bookmarks", use_profile=False, help=_("manage bookmarks")
+        )
--- a/sat_frontends/jp/cmd_debug.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_debug.py	Mon Jun 14 18:35:12 2021 +0200
@@ -53,20 +53,19 @@
         self.parser.add_argument(
             "method", type=str, help=_("name of the method to execute")
         )
-        self.parser.add_argument(
-            "arg", nargs="*", help=_("argument of the method")
-        )
+        self.parser.add_argument("arg", nargs="*", help=_("argument of the method"))
 
     async def start(self):
         method = getattr(self.host.bridge, self.args.method)
         import inspect
+
         argspec = inspect.getargspec(method)
 
         kwargs = {}
-        if 'profile_key' in argspec.args:
-            kwargs['profile_key'] = self.profile
-        elif 'profile' in argspec.args:
-            kwargs['profile'] = self.profile
+        if "profile_key" in argspec.args:
+            kwargs["profile_key"] = self.profile
+        elif "profile" in argspec.args:
+            kwargs["profile"] = self.profile
 
         args = self.evalArgs()
 
@@ -76,7 +75,12 @@
                 **kwargs,
             )
         except Exception as e:
-            self.disp(_(f"Error while executing {self.args.method}: {e}"), error=True)
+            self.disp(
+                _("Error while executing {method}: {e}").format(
+                    method=self.args.method, e=e
+                ),
+                error=True,
+            )
             self.host.quit(C.EXIT_ERROR)
         else:
             if ret is not None:
@@ -92,12 +96,8 @@
         BridgeCommon.__init__(self)
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "signal", type=str, help=_("name of the signal to send")
-        )
-        self.parser.add_argument(
-            "arg", nargs="*", help=_("argument of the signal")
-        )
+        self.parser.add_argument("signal", type=str, help=_("name of the signal to send"))
+        self.parser.add_argument("arg", nargs="*", help=_("argument of the signal"))
 
     async def start(self):
         args = self.evalArgs()
@@ -105,9 +105,11 @@
         # XXX: we use self.args.profile and not self.profile
         #      because we want the raw profile_key (so plugin handle C.PROF_KEY_NONE)
         try:
-            await self.host.bridge.debugFakeSignal(self.args.signal, json_args, self.args.profile)
+            await self.host.bridge.debugFakeSignal(
+                self.args.signal, json_args, self.args.profile
+            )
         except Exception as e:
-            self.disp(_(f"Can't send fake signal: {e}"), error=True)
+            self.disp(_("Can't send fake signal: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_ERROR)
         else:
             self.host.quit()
@@ -198,18 +200,17 @@
 
     async def start(self):
         for attr in dir(C):
-            if not attr.startswith('A_'):
+            if not attr.startswith("A_"):
                 continue
             color = getattr(C, attr)
-            if attr == 'A_LEVEL_COLORS':
+            if attr == "A_LEVEL_COLORS":
                 # This constant contains multiple colors
-                self.disp('LEVEL COLORS: ', end=' ')
+                self.disp("LEVEL COLORS: ", end=" ")
                 for idx, c in enumerate(color):
-                    last = idx == len(color)-1
-                    end = '\n' if last else ' '
+                    last = idx == len(color) - 1
+                    end = "\n" if last else " "
                     self.disp(
-                        c + f'LEVEL_{idx}' + A.RESET + (', ' if not last else ''),
-                        end=end
+                        c + f"LEVEL_{idx}" + A.RESET + (", " if not last else ""), end=end
                     )
             else:
                 text = attr[2:]
--- a/sat_frontends/jp/cmd_event.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_event.py	Mon Jun 14 18:35:12 2021 +0200
@@ -107,9 +107,7 @@
             default="",
             help=_("ID of the PubSub Item"),
         )
-        self.parser.add_argument(
-            "-d", "--date", type=str, help=_("date of the event")
-        )
+        self.parser.add_argument("-d", "--date", type=str, help=_("date of the event"))
         self.parser.add_argument(
             "-f",
             "--field",
@@ -169,7 +167,7 @@
             self.disp(f"can't create event: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"Event created successfuly on node {node}"))
+            self.disp(_("Event created successfuly on node {node}").format(node=node))
             self.host.quit()
 
 
@@ -350,9 +348,7 @@
         table = common.Table.fromListDict(
             self.host,
             data,
-            ("nick",)
-            + (("jid",) if self.host.verbosity else ())
-            + ("attend", "guests"),
+            ("nick",) + (("jid",) if self.host.verbosity else ()) + ("attend", "guests"),
             headers=None,
             filters={
                 "nick": A.color(C.A_HEADER, "{}" if show_table else "{} "),
@@ -402,9 +398,7 @@
                 )
             )
             self.disp(
-                A.color(
-                    C.A_SUBHEADER, _("total: "), A.RESET, str(guests + guests_maybe)
-                )
+                A.color(C.A_SUBHEADER, _("total: "), A.RESET, str(guests + guests_maybe))
             )
         if attendees_missing:
             self.disp("")
@@ -453,7 +447,7 @@
             self.disp(f"can't get event data: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
-        # we fill nicknames and keep only requested people
+            # we fill nicknames and keep only requested people
 
         if self.args.no_rsvp:
             for jid_ in event_data:
@@ -462,21 +456,23 @@
                 try:
                     del prefilled[jid_]
                 except KeyError:
-                    self.disp(A.color(
-                        C.A_WARNING,
-                        f"We got a RSVP from somebody who was not in invitees "
-                        f"list: {jid_}"
+                    self.disp(
+                        A.color(
+                            C.A_WARNING,
+                            f"We got a RSVP from somebody who was not in invitees "
+                            f"list: {jid_}",
                         ),
-                        error=True)
+                        error=True,
+                    )
         else:
             # we replace empty dicts for existing people with R.S.V.P. data
             prefilled.update(event_data)
 
-        # we get nicknames for everybody, make it easier for organisers
+            # we get nicknames for everybody, make it easier for organisers
         for jid_, data in prefilled.items():
             id_data = await self.host.bridge.identityGet(jid_, [], True, self.profile)
             id_data = data_format.deserialise(id_data)
-            data["nick"] = id_data['nicknames'][0]
+            data["nick"] = id_data["nicknames"][0]
 
         await self.output(prefilled)
         self.host.quit()
--- a/sat_frontends/jp/cmd_file.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_file.py	Mon Jun 14 18:35:12 2021 +0200
@@ -54,9 +54,7 @@
         self.parser.add_argument(
             "files", type=str, nargs="+", metavar="file", help=_("a list of file")
         )
-        self.parser.add_argument(
-            "jid", help=_("the destination jid")
-        )
+        self.parser.add_argument("jid", help=_("the destination jid"))
         self.parser.add_argument(
             "-b", "--bz2", action="store_true", help=_("make a bzip2 tarball")
         )
@@ -101,19 +99,21 @@
             await self.set_progress_id(data["progress"])
         except KeyError:
             # TODO: if 'xmlui' key is present, manage xmlui message display
-            self.disp(
-                _("Can't send file to {jid}".format(jid=self.args.jid)), error=True
-            )
+            self.disp(_("Can't send file to {jid}".format(jid=self.args.jid)), error=True)
             self.host.quit(2)
 
     async def start(self):
         for file_ in self.args.files:
             if not os.path.exists(file_):
-                self.disp(_(f"file {file_!r} doesn't exist!"), error=True)
+                self.disp(
+                    _("file {file_} doesn't exist!").format(file_=repr(file_)), error=True
+                )
                 self.host.quit(C.EXIT_BAD_ARG)
             if not self.args.bz2 and os.path.isdir(file_):
                 self.disp(
-                    _(f"{file_!r} is a dir! Please send files inside or use compression")
+                    _(
+                        "{file_} is a dir! Please send files inside or use compression"
+                    ).format(file_=repr(file_))
                 )
                 self.host.quit(C.EXIT_BAD_ARG)
 
@@ -188,9 +188,7 @@
         return self.args.name or self.args.hash or "output"
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "jid", help=_("the destination jid")
-        )
+        self.parser.add_argument("jid", help=_("the destination jid"))
         self.parser.add_argument(
             "-D",
             "--dest",
@@ -258,7 +256,8 @@
 
         if os.path.exists(path) and not self.args.force:
             message = _("File {path} already exists! Do you want to overwrite?").format(
-                path = path)
+                path=path
+            )
             await self.host.confirmOrQuit(message, _("file request cancelled"))
 
         self.full_dest_jid = await self.host.get_full_jid(self.args.jid)
@@ -278,7 +277,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(msg=_(f"can't request file: {e}"), error=True)
+            self.disp(msg=_("can't request file: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             await self.set_progress_id(progress_id)
@@ -334,15 +333,16 @@
         self.disp(_("File received successfully"), 2)
         if metadata.get("hash_verified", False):
             try:
-                self.disp(_(
-                    f"hash checked: {metadata['hash_algo']}:{metadata['hash']}"), 1)
+                self.disp(
+                    _("hash checked: {metadata['hash_algo']}:{metadata['hash']}"), 1
+                )
             except KeyError:
                 self.disp(_("hash is checked but hash value is missing", 1), error=True)
         else:
             self.disp(_("hash can't be verified"), 1)
 
     async def onProgressError(self, e):
-        self.disp(_(f"Error while receiving file: {e}"), error=True)
+        self.disp(_("Error while receiving file: {e}").format(e=e), error=True)
 
     def getXmluiId(self, action_data):
         # FIXME: we temporarily use ElementTree, but a real XMLUI managing module
@@ -413,8 +413,12 @@
         try:
             from_jid = jid.JID(action_data["meta_from_jid"])
         except ValueError:
-            self.disp(_('invalid "from_jid" value received, ignoring: {value}').format(
-                value=from_jid), error=True)
+            self.disp(
+                _('invalid "from_jid" value received, ignoring: {value}').format(
+                    value=from_jid
+                ),
+                error=True,
+            )
             return
         except KeyError:
             self.disp(_('ignoring action without "from_jid" value'), error=True)
@@ -448,18 +452,23 @@
 
 
 class Get(base.CommandBase):
-
     def __init__(self, host):
         super(Get, self).__init__(
-            host, "get", use_progress=True, use_verbose=True,
-            help=_("download a file from URI")
+            host,
+            "get",
+            use_progress=True,
+            use_verbose=True,
+            help=_("download a file from URI"),
         )
 
     def add_parser_options(self):
         self.parser.add_argument(
-            '-o', '--dest-file', type=str, default='',
-            help=_("destination file (DEFAULT: filename from URL)")
-            )
+            "-o",
+            "--dest-file",
+            type=str,
+            default="",
+            help=_("destination file (DEFAULT: filename from URL)"),
+        )
         self.parser.add_argument(
             "-f",
             "--force",
@@ -482,8 +491,8 @@
         try:
             await self.set_progress_id(data["progress"])
         except KeyError:
-            if 'xmlui' in data:
-                ui = xmlui_manager.create(self.host, data['xmlui'])
+            if "xmlui" in data:
+                ui = xmlui_manager.create(self.host, data["xmlui"])
                 await ui.show()
             else:
                 self.disp(_("Can't download file"), error=True)
@@ -499,7 +508,8 @@
         dest_file = Path(dest_file).expanduser().resolve()
         if dest_file.exists() and not self.args.force:
             message = _("File {path} already exists! Do you want to overwrite?").format(
-                path = dest_file)
+                path=dest_file
+            )
             await self.host.confirmOrQuit(message, _("file download cancelled"))
 
         options = {}
@@ -569,8 +579,8 @@
         try:
             await self.set_progress_id(data["progress"])
         except KeyError:
-            if 'xmlui' in data:
-                ui = xmlui_manager.create(self.host, data['xmlui'])
+            if "xmlui" in data:
+                ui = xmlui_manager.create(self.host, data["xmlui"])
                 await ui.show()
             else:
                 self.disp(_("Can't upload file"), error=True)
@@ -579,10 +589,12 @@
     async def start(self):
         file_ = self.args.file
         if not os.path.exists(file_):
-            self.disp(_(f"file {file_!r} doesn't exist !"), error=True)
+            self.disp(
+                _("file {file_} doesn't exist !").format(file_=repr(file_)), error=True
+            )
             self.host.quit(C.EXIT_BAD_ARG)
         if os.path.isdir(file_):
-            self.disp(_(f"{file_!r} is a dir! Can't upload a dir"))
+            self.disp(_("{file_} is a dir! Can't upload a dir").format(file_=repr(file_)))
             self.host.quit(C.EXIT_BAD_ARG)
 
         if self.args.jid is None:
@@ -613,7 +625,6 @@
 
 
 class ShareAffiliationsSet(base.CommandBase):
-
     def __init__(self, host):
         super(ShareAffiliationsSet, self).__init__(
             host,
@@ -720,7 +731,6 @@
 
 
 class ShareConfigurationSet(base.CommandBase):
-
     def __init__(self, host):
         super(ShareConfigurationSet, self).__init__(
             host,
@@ -822,8 +832,10 @@
 
     def __init__(self, host):
         super(ShareConfiguration, self).__init__(
-            host, "configuration", use_profile=False,
-            help=_("file sharing node configuration")
+            host,
+            "configuration",
+            use_profile=False,
+            help=_("file sharing node configuration"),
         )
 
 
@@ -962,7 +974,11 @@
             self.disp(f"can't share path: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f'{self.path} shared under the name "{name}"'))
+            self.disp(
+                _('{path} shared under the name "{name}"').format(
+                    path=self.path, name=name
+                )
+            )
             self.host.quit()
 
 
@@ -1015,10 +1031,10 @@
         self.path = os.path.normpath(self.args.path) if self.args.path else ""
         extra = {}
         if self.args.thumbnail is not None:
-            if not self.args.thumbnail.startswith('http'):
+            if not self.args.thumbnail.startswith("http"):
                 self.parser.error(_("only http(s) links are allowed with --thumbnail"))
             else:
-                extra['thumb_url'] = self.args.thumbnail
+                extra["thumb_url"] = self.args.thumbnail
         try:
             await self.host.bridge.FISInvite(
                 self.args.jid,
@@ -1034,15 +1050,18 @@
             self.disp(f"can't send invitation: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(
-                _(f'invitation sent to {self.args.jid}')
-            )
+            self.disp(_("invitation sent to {jid}").format(jid=self.args.jid))
             self.host.quit()
 
 
 class Share(base.CommandBase):
     subcommands = (
-        ShareList, SharePath, ShareInvite, ShareAffiliations, ShareConfiguration)
+        ShareList,
+        SharePath,
+        ShareInvite,
+        ShareAffiliations,
+        ShareConfiguration,
+    )
 
     def __init__(self, host):
         super(Share, self).__init__(
--- a/sat_frontends/jp/cmd_info.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_info.py	Mon Jun 14 18:35:12 2021 +0200
@@ -28,35 +28,47 @@
 
 
 class Disco(base.CommandBase):
-
     def __init__(self, host):
-        extra_outputs = {'default': self.default_output}
+        extra_outputs = {"default": self.default_output}
         super(Disco, self).__init__(
-            host, 'disco', use_output='complex', extra_outputs=extra_outputs,
-            help=_('service discovery'))
+            host,
+            "disco",
+            use_output="complex",
+            extra_outputs=extra_outputs,
+            help=_("service discovery"),
+        )
 
     def add_parser_options(self):
         self.parser.add_argument("jid", help=_("entity to discover"))
         self.parser.add_argument(
-            "-t", "--type", type=str, choices=('infos', 'items', 'both'), default='both',
-            help=_("type of data to discover"))
-        self.parser.add_argument("-n", "--node", default='', help=_("node to use"))
+            "-t",
+            "--type",
+            type=str,
+            choices=("infos", "items", "both"),
+            default="both",
+            help=_("type of data to discover"),
+        )
+        self.parser.add_argument("-n", "--node", default="", help=_("node to use"))
         self.parser.add_argument(
-            "-C", "--no-cache", dest='use_cache', action="store_false",
-            help=_("ignore cache"))
+            "-C",
+            "--no-cache",
+            dest="use_cache",
+            action="store_false",
+            help=_("ignore cache"),
+        )
 
     def default_output(self, data):
-        features = data.get('features', [])
-        identities = data.get('identities', [])
-        extensions = data.get('extensions', {})
-        items = data.get('items', [])
+        features = data.get("features", [])
+        identities = data.get("identities", [])
+        extensions = data.get("extensions", {})
+        items = data.get("items", [])
 
-        identities_table = common.Table(self.host,
-                                        identities,
-                                        headers=(_('category'),
-                                                 _('type'),
-                                                 _('name')),
-                                        use_buffer=True)
+        identities_table = common.Table(
+            self.host,
+            identities,
+            headers=(_("category"), _("type"), _("name")),
+            use_buffer=True,
+        )
 
         extensions_tpl = []
         extensions_types = list(extensions.keys())
@@ -69,30 +81,35 @@
                 data_keys = list(data.keys())
                 data_keys.sort()
                 for key in data_keys:
-                    field_lines.append(A.color('\t', C.A_SUBHEADER, key, A.RESET, ': ',
-                                               data[key]))
+                    field_lines.append(
+                        A.color("\t", C.A_SUBHEADER, key, A.RESET, ": ", data[key])
+                    )
                 if len(values) == 1:
-                    field_lines.append(A.color('\t', C.A_SUBHEADER, "value", A.RESET,
-                                               ': ', values[0] or (A.BOLD + "UNSET")))
+                    field_lines.append(
+                        A.color(
+                            "\t",
+                            C.A_SUBHEADER,
+                            "value",
+                            A.RESET,
+                            ": ",
+                            values[0] or (A.BOLD + "UNSET"),
+                        )
+                    )
                 elif len(values) > 1:
-                    field_lines.append(A.color('\t', C.A_SUBHEADER, "values", A.RESET,
-                                               ': '))
+                    field_lines.append(
+                        A.color("\t", C.A_SUBHEADER, "values", A.RESET, ": ")
+                    )
 
                     for value in values:
-                        field_lines.append(A.color('\t  - ', A.BOLD, value))
-                fields.append('\n'.join(field_lines))
-            extensions_tpl.append('{type_}\n{fields}'.format(
-                type_=type_,
-                fields='\n\n'.join(fields)))
+                        field_lines.append(A.color("\t  - ", A.BOLD, value))
+                fields.append("\n".join(field_lines))
+            extensions_tpl.append(
+                "{type_}\n{fields}".format(type_=type_, fields="\n\n".join(fields))
+            )
 
         items_table = common.Table(
-            self.host,
-            items,
-            headers=(
-                _('entity'),
-                _('node'),
-                _('name')),
-            use_buffer=True)
+            self.host, items, headers=(_("entity"), _("node"), _("name")), use_buffer=True
+        )
 
         template = []
         if features:
@@ -104,16 +121,18 @@
         if items:
             template.append(A.color(C.A_HEADER, _("Items")) + "\n\n{items}")
 
-        print("\n\n".join(template).format(
-            features = '\n'.join(features),
-            identities = identities_table.display().string,
-            extensions = '\n'.join(extensions_tpl),
-            items = items_table.display().string,
-            ))
+        print(
+            "\n\n".join(template).format(
+                features="\n".join(features),
+                identities=identities_table.display().string,
+                extensions="\n".join(extensions_tpl),
+                items=items_table.display().string,
+            )
+        )
 
     async def start(self):
-        infos_requested = self.args.type in ('infos', 'both')
-        items_requested = self.args.type in ('items', 'both')
+        infos_requested = self.args.type in ("infos", "both")
+        items_requested = self.args.type in ("items", "both")
         jids = await self.host.check_jids([self.args.jid])
         jid = jids[0]
 
@@ -126,13 +145,13 @@
                     jid,
                     node=self.args.node,
                     use_cache=self.args.use_cache,
-                    profile_key=self.host.profile
+                    profile_key=self.host.profile,
                 )
             except Exception as e:
-                self.disp(_(f"error while doing discovery: {e}"), error=True)
+                self.disp(_("error while doing discovery: {e}").format(e=e), error=True)
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
-        # items
+                # items
         if not items_requested:
             items = None
         else:
@@ -141,35 +160,34 @@
                     jid,
                     node=self.args.node,
                     use_cache=self.args.use_cache,
-                    profile_key=self.host.profile
+                    profile_key=self.host.profile,
                 )
             except Exception as e:
-                self.disp(_(f"error while doing discovery: {e}"), error=True)
+                self.disp(_("error while doing discovery: {e}").format(e=e), error=True)
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
-        # output
+                # output
         data = {}
 
         if infos_requested:
             features, identities, extensions = infos
             features.sort()
             identities.sort(key=lambda identity: identity[2])
-            data.update({
-                'features': features,
-                'identities': identities,
-                'extensions': extensions})
+            data.update(
+                {"features": features, "identities": identities, "extensions": extensions}
+            )
 
         if items_requested:
             items.sort(key=lambda item: item[2])
-            data['items'] = items
+            data["items"] = items
 
         await self.output(data)
         self.host.quit()
 
+
 class Version(base.CommandBase):
-
     def __init__(self, host):
-        super(Version, self).__init__(host, 'version', help=_('software version'))
+        super(Version, self).__init__(host, "version", help=_("software version"))
 
     def add_parser_options(self):
         self.parser.add_argument("jid", type=str, help=_("Entity to request"))
@@ -180,45 +198,49 @@
         try:
             data = await self.host.bridge.getSoftwareVersion(jid, self.host.profile)
         except Exception as e:
-            self.disp(_(f"error while trying to get version: {e}"), error=True)
+            self.disp(_("error while trying to get version: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             infos = []
             name, version, os = data
             if name:
-                infos.append(_(f"Software name: {name}"))
+                infos.append(_("Software name: {name}").format(name=name))
             if version:
-                infos.append(_(f"Software version: {version}"))
+                infos.append(_("Software version: {version}").format(version=version))
             if os:
-                infos.append(_(f"Operating System: {os}"))
+                infos.append(_("Operating System: {os}").format(os=os))
 
             print("\n".join(infos))
             self.host.quit()
 
 
 class Session(base.CommandBase):
-
     def __init__(self, host):
-        extra_outputs = {'default': self.default_output}
+        extra_outputs = {"default": self.default_output}
         super(Session, self).__init__(
-            host, 'session', use_output='dict', extra_outputs=extra_outputs,
-            help=_('running session'))
+            host,
+            "session",
+            use_output="dict",
+            extra_outputs=extra_outputs,
+            help=_("running session"),
+        )
 
     def add_parser_options(self):
         pass
 
     async def default_output(self, data):
-        started = data['started']
-        data['started'] = '{short} (UTC, {relative})'.format(
+        started = data["started"]
+        data["started"] = "{short} (UTC, {relative})".format(
             short=date_utils.date_fmt(started),
-            relative=date_utils.date_fmt(started, 'relative'))
-        await self.host.output(C.OUTPUT_DICT, 'simple', {}, data)
+            relative=date_utils.date_fmt(started, "relative"),
+        )
+        await self.host.output(C.OUTPUT_DICT, "simple", {}, data)
 
     async def start(self):
         try:
             data = await self.host.bridge.sessionInfosGet(self.host.profile)
         except Exception as e:
-            self.disp(_(f'Error getting session infos: {e}'), error=True)
+            self.disp(_("Error getting session infos: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             await self.output(data)
@@ -226,22 +248,23 @@
 
 
 class Devices(base.CommandBase):
-
     def __init__(self, host):
         super(Devices, self).__init__(
-            host, 'devices', use_output=C.OUTPUT_LIST_DICT,
-            help=_('devices of an entity'))
+            host, "devices", use_output=C.OUTPUT_LIST_DICT, help=_("devices of an entity")
+        )
 
     def add_parser_options(self):
         self.parser.add_argument(
-            "jid", type=str, nargs='?', default='', help=_("Entity to request"))
+            "jid", type=str, nargs="?", default="", help=_("Entity to request")
+        )
 
     async def start(self):
         try:
             data = await self.host.bridge.devicesInfosGet(
-                self.args.jid, self.host.profile)
+                self.args.jid, self.host.profile
+            )
         except Exception as e:
-            self.disp(_(f'Error getting devices infos: {e}'), error=True)
+            self.disp(_("Error getting devices infos: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             data = data_format.deserialise(data, type_check=list)
@@ -254,5 +277,8 @@
 
     def __init__(self, host):
         super(Info, self).__init__(
-            host, 'info', use_profile=False,
-            help=_('Get various pieces of information on entities'))
+            host,
+            "info",
+            use_profile=False,
+            help=_("Get various pieces of information on entities"),
+        )
--- a/sat_frontends/jp/cmd_invitation.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_invitation.py	Mon Jun 14 18:35:12 2021 +0200
@@ -162,9 +162,7 @@
         )
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "id", help=_("invitation UUID")
-        )
+        self.parser.add_argument("id", help=_("invitation UUID"))
         self.parser.add_argument(
             "-j",
             "--with-jid",
@@ -184,7 +182,7 @@
                 self.args.id,
             )
         except Exception as e:
-            self.disp(msg=_(f"can't get invitation data: {e}"), error=True)
+            self.disp(msg=_("can't get invitation data: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
         if not self.args.with_jid:
@@ -197,7 +195,7 @@
                     profile,
                 )
             except Exception as e:
-                self.disp(msg=_(f"can't start session: {e}"), error=True)
+                self.disp(msg=_("can't start session: {e}").format(e=e), error=True)
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
             try:
@@ -207,7 +205,7 @@
                     profile_key=profile,
                 )
             except Exception as e:
-                self.disp(msg=_(f"can't retrieve jid: {e}"), error=True)
+                self.disp(msg=_("can't retrieve jid: {e}").format(e=e), error=True)
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
             await self.output_data(invitation_data, jid_)
@@ -224,9 +222,7 @@
         )
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "id", help=_("invitation UUID")
-        )
+        self.parser.add_argument("id", help=_("invitation UUID"))
 
     async def start(self):
         try:
@@ -234,7 +230,7 @@
                 self.args.id,
             )
         except Exception as e:
-            self.disp(msg=_(f"can't delete guest account: {e}"), error=True)
+            self.disp(msg=_("can't delete guest account: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
         self.host.quit()
@@ -290,9 +286,7 @@
             default="",
             help="profile doing the invitation (default: don't associate profile",
         )
-        self.parser.add_argument(
-            "id", help=_("invitation UUID")
-        )
+        self.parser.add_argument("id", help=_("invitation UUID"))
 
     async def start(self):
         extra = dict(self.args.extra)
@@ -302,7 +296,9 @@
                 continue
             if arg_name in extra:
                 self.parser.error(
-                    _(f"you can't set {arg_name} in both optional argument and extra")
+                    _(
+                        "you can't set {arg_name} in both optional argument and extra"
+                    ).format(arg_name=arg_name)
                 )
             extra[arg_name] = value
         try:
@@ -312,9 +308,7 @@
                 self.args.replace,
             )
         except Exception as e:
-            self.disp(
-                f"can't modify invitation: {e}", error=True
-            )
+            self.disp(f"can't modify invitation: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp(_("invitations have been modified successfuly"))
--- a/sat_frontends/jp/cmd_list.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_list.py	Mon Jun 14 18:35:12 2021 +0200
@@ -61,7 +61,7 @@
                     self.getPubsubExtra(),
                     self.profile,
                 ),
-                type_check=list
+                type_check=list,
             )
         except Exception as e:
             self.disp(f"can't get lists: {e}", error=True)
@@ -72,7 +72,6 @@
 
 
 class Set(base.CommandBase):
-
     def __init__(self, host):
         base.CommandBase.__init__(
             self,
@@ -128,10 +127,10 @@
                 self.args.service,
                 self.args.node,
                 values,
-                '',
+                "",
                 self.args.item,
                 data_format.serialise(extra),
-                self.profile
+                self.profile,
             )
         except Exception as e:
             self.disp(f"can't set list item: {e}", error=True)
@@ -142,7 +141,6 @@
 
 
 class Delete(base.CommandBase):
-
     def __init__(self, host):
         base.CommandBase.__init__(
             self,
@@ -183,10 +181,10 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't delete item: {e}"), error=True)
+            self.disp(_("can't delete item: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"item {self.args.item} has been deleted"))
+            self.disp(_("item {item} has been deleted").format(item=self.args.item))
             self.host.quit(C.EXIT_OK)
 
 
@@ -262,7 +260,10 @@
         self.disp(_("Tickets uploaded successfully"), 2)
 
     async def onProgressError(self, error_msg):
-        self.disp(_(f"Error while uploading tickets: {error_msg}"), error=True)
+        self.disp(
+            _("Error while uploading tickets: {error_msg}").format(error_msg=error_msg),
+            error=True,
+        )
 
     async def start(self):
         if self.args.location is None:
@@ -270,7 +271,10 @@
             for name in ("option", "service", "node"):
                 if getattr(self.args, name):
                     self.parser.error(
-                        _(f"{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(
                     "\n".join(
@@ -297,8 +301,14 @@
             if self.args.progress:
                 # we use a custom progress bar template as we want a counter
                 self.pbar_template = [
-                    _("Progress: "), ["Percentage"], " ", ["Bar"], " ",
-                    ["Counter"], " ", ["ETA"]
+                    _("Progress: "),
+                    ["Percentage"],
+                    " ",
+                    ["Bar"],
+                    " ",
+                    ["Counter"],
+                    " ",
+                    ["ETA"],
                 ]
 
             options = {key: value for key, value in self.args.option}
@@ -306,8 +316,10 @@
             if fields_map:
                 if FIELDS_MAP in options:
                     self.parser.error(
-                        _("fields_map must be specified either preencoded in --option or "
-                          "using --map, but not both at the same time")
+                        _(
+                            "fields_map must be specified either preencoded in --option or "
+                            "using --map, but not both at the same time"
+                        )
                     )
                 options[FIELDS_MAP] = json.dumps(fields_map)
 
@@ -322,7 +334,7 @@
                 )
             except Exception as e:
                 self.disp(
-                    _(f"Error while trying to import tickets: {e}"),
+                    _("Error while trying to import tickets: {e}").format(e=e),
                     error=True,
                 )
                 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
--- a/sat_frontends/jp/cmd_merge_request.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_merge_request.py	Mon Jun 14 18:35:12 2021 +0200
@@ -30,7 +30,6 @@
 
 
 class Set(base.CommandBase):
-
     def __init__(self, host):
         base.CommandBase.__init__(
             self,
@@ -74,11 +73,12 @@
         await common.fill_well_known_uri(self, self.repository, "merge requests")
         if not self.args.force:
             message = _(
-                f"You are going to publish your changes to service "
-                f"[{self.args.service}], are you sure ?"
+                "You are going to publish your changes to service "
+                "[{service}], are you sure ?"
+            ).format(service=self.args.service)
+            await self.host.confirmOrQuit(
+                message, _("merge request publication cancelled")
             )
-            await self.host.confirmOrQuit(
-                message, _("merge request publication cancelled"))
 
         extra = {"update": True} if self.args.item else {}
         values = {}
@@ -101,7 +101,11 @@
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
         if published_id:
-            self.disp(_(f"Merge request published at {published_id}"))
+            self.disp(
+                _("Merge request published at {published_id}").format(
+                    published_id=published_id
+                )
+            )
         else:
             self.disp(_("Merge request published"))
 
@@ -125,8 +129,7 @@
         pass
 
     async def start(self):
-        await common.fill_well_known_uri(
-            self, os.getcwd(), "merge requests", meta_map={})
+        await common.fill_well_known_uri(self, os.getcwd(), "merge requests", meta_map={})
         extra = {}
         try:
             requests_data = data_format.deserialise(
@@ -148,7 +151,7 @@
             whitelist = None
         else:
             whitelist = {"id", "title", "body"}
-        for request_xmlui in requests_data['items']:
+        for request_xmlui in requests_data["items"]:
             xmlui = xmlui_manager.create(self.host, request_xmlui, whitelist=whitelist)
             await xmlui.show(values_only=True)
             self.disp("")
@@ -179,7 +182,8 @@
     async def start(self):
         self.repository = os.path.expanduser(os.path.abspath(self.args.repository))
         await common.fill_well_known_uri(
-            self, self.repository, "merge requests", meta_map={})
+            self, self.repository, "merge requests", meta_map={}
+        )
         extra = {}
         try:
             await self.host.bridge.mergeRequestsImport(
--- a/sat_frontends/jp/cmd_param.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_param.py	Mon Jun 14 18:35:12 2021 +0200
@@ -22,23 +22,31 @@
 from . import base
 from sat.core.i18n import _
 from .constants import Const as C
+
 __commands__ = ["Param"]
 
 
 class Get(base.CommandBase):
     def __init__(self, host):
         super(Get, self).__init__(
-            host, 'get', need_connect=False, help=_('get a parameter value'))
+            host, "get", need_connect=False, help=_("get a parameter value")
+        )
 
     def add_parser_options(self):
         self.parser.add_argument(
-            "category", nargs='?', help=_("category of the parameter"))
-        self.parser.add_argument("name", nargs='?', help=_("name of the parameter"))
+            "category", nargs="?", help=_("category of the parameter")
+        )
+        self.parser.add_argument("name", nargs="?", help=_("name of the parameter"))
         self.parser.add_argument(
-            "-a", "--attribute", type=str, default="value",
-            help=_("name of the attribute to get"))
+            "-a",
+            "--attribute",
+            type=str,
+            default="value",
+            help=_("name of the attribute to get"),
+        )
         self.parser.add_argument(
-            "--security-limit", type=int, default=-1, help=_("security limit"))
+            "--security-limit", type=int, default=-1, help=_("security limit")
+        )
 
     async def start(self):
         if self.args.category is None:
@@ -47,9 +55,12 @@
         elif self.args.name is None:
             try:
                 values_dict = await self.host.bridge.asyncGetParamsValuesFromCategory(
-                    self.args.category, self.args.security_limit, "", "", self.profile)
+                    self.args.category, self.args.security_limit, "", "", self.profile
+                )
             except Exception as e:
-                self.disp(_(f"can't find requested parameters: {e}"), error=True)
+                self.disp(
+                    _("can't find requested parameters: {e}").format(e=e), error=True
+                )
                 self.host.quit(C.EXIT_NOT_FOUND)
             else:
                 for name, value in values_dict.items():
@@ -57,10 +68,16 @@
         else:
             try:
                 value = await self.host.bridge.asyncGetParamA(
-                    self.args.name, self.args.category, self.args.attribute,
-                    self.args.security_limit, self.profile)
+                    self.args.name,
+                    self.args.category,
+                    self.args.attribute,
+                    self.args.security_limit,
+                    self.profile,
+                )
             except Exception as e:
-                self.disp(_(f"can't find requested parameter: {e}"), error=True)
+                self.disp(
+                    _("can't find requested parameter: {e}").format(e=e), error=True
+                )
                 self.host.quit(C.EXIT_NOT_FOUND)
             else:
                 print(value)
@@ -69,21 +86,29 @@
 
 class Set(base.CommandBase):
     def __init__(self, host):
-        super(Set, self).__init__(host, 'set', need_connect=False, help=_('set a parameter value'))
+        super(Set, self).__init__(
+            host, "set", need_connect=False, help=_("set a parameter value")
+        )
 
     def add_parser_options(self):
         self.parser.add_argument("category", help=_("category of the parameter"))
         self.parser.add_argument("name", help=_("name of the parameter"))
         self.parser.add_argument("value", help=_("name of the parameter"))
-        self.parser.add_argument("--security-limit", type=int, default=-1, help=_("security limit"))
+        self.parser.add_argument(
+            "--security-limit", type=int, default=-1, help=_("security limit")
+        )
 
     async def start(self):
         try:
             await self.host.bridge.setParam(
-                self.args.name, self.args.value, self.args.category,
-                self.args.security_limit, self.profile)
+                self.args.name,
+                self.args.value,
+                self.args.category,
+                self.args.security_limit,
+                self.profile,
+            )
         except Exception as e:
-            self.disp(_(f"can't set requested parameter: {e}"), error=True)
+            self.disp(_("can't set requested parameter: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.host.quit()
@@ -94,8 +119,11 @@
 
     def __init__(self, host):
         super(SaveTemplate, self).__init__(
-            host, 'save', use_profile=False,
-            help=_('save parameters template to xml file'))
+            host,
+            "save",
+            use_profile=False,
+            help=_("save parameters template to xml file"),
+        )
 
     def add_parser_options(self):
         self.parser.add_argument("filename", type=str, help=_("output file"))
@@ -105,10 +133,14 @@
         try:
             await self.host.bridge.saveParamsTemplate(self.args.filename)
         except Exception as e:
-            self.disp(_(f"can't save parameters to file: {e}"), error=True)
+            self.disp(_("can't save parameters to file: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"parameters saved to file {self.args.filename}"))
+            self.disp(
+                _("parameters saved to file {filename}").format(
+                    filename=self.args.filename
+                )
+            )
             self.host.quit()
 
 
@@ -117,8 +149,11 @@
 
     def __init__(self, host):
         super(LoadTemplate, self).__init__(
-            host, 'load', use_profile=False,
-            help=_('load parameters template from xml file'))
+            host,
+            "load",
+            use_profile=False,
+            help=_("load parameters template from xml file"),
+        )
 
     def add_parser_options(self):
         self.parser.add_argument("filename", type=str, help=_("input file"))
@@ -128,10 +163,14 @@
         try:
             self.host.bridge.loadParamsTemplate(self.args.filename)
         except Exception as e:
-            self.disp(_(f"can't load parameters from file: {e}"), error=True)
+            self.disp(_("can't load parameters from file: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"parameters loaded from file {self.args.filename}"))
+            self.disp(
+                _("parameters loaded from file {filename}").format(
+                    filename=self.args.filename
+                )
+            )
             self.host.quit()
 
 
@@ -139,4 +178,6 @@
     subcommands = (Get, Set, SaveTemplate, LoadTemplate)
 
     def __init__(self, host):
-        super(Param, self).__init__(host, 'param', use_profile=False, help=_('Save/load parameters template'))
+        super(Param, self).__init__(
+            host, "param", use_profile=False, help=_("Save/load parameters template")
+        )
--- a/sat_frontends/jp/cmd_ping.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_ping.py	Mon Jun 14 18:35:12 2021 +0200
@@ -25,14 +25,11 @@
 
 
 class Ping(base.CommandBase):
-
     def __init__(self, host):
-        super(Ping, self).__init__(host, 'ping', help=_('ping XMPP entity'))
+        super(Ping, self).__init__(host, "ping", help=_("ping XMPP entity"))
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "jid", help=_("jid to ping")
-        )
+        self.parser.add_argument("jid", help=_("jid to ping"))
         self.parser.add_argument(
             "-d", "--delay-only", action="store_true", help=_("output delay only (in s)")
         )
@@ -41,9 +38,9 @@
         try:
             pong_time = await self.host.bridge.ping(self.args.jid, self.profile)
         except Exception as e:
-            self.disp(msg=_(f"can't do the ping: {e}"), error=True)
+            self.disp(msg=_("can't do the ping: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            msg = pong_time if self.args.delay_only else  f"PONG ({pong_time} s)"
+            msg = pong_time if self.args.delay_only else f"PONG ({pong_time} s)"
             self.disp(msg)
             self.host.quit()
--- a/sat_frontends/jp/cmd_pubsub.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/cmd_pubsub.py	Mon Jun 14 18:35:12 2021 +0200
@@ -82,10 +82,10 @@
                 self.profile,
             )
         except BridgeException as e:
-            if e.condition == 'item-not-found':
+            if e.condition == "item-not-found":
                 self.disp(
                     f"The node {self.args.node} doesn't exist on {self.args.service}",
-                    error=True
+                    error=True,
                 )
                 self.host.quit(C.EXIT_NOT_FOUND)
             else:
@@ -155,7 +155,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(msg=_(f"can't create node: {e}"), error=True)
+            self.disp(msg=_("can't create node: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             if self.host.verbosity:
@@ -167,7 +167,6 @@
 
 
 class NodePurge(base.CommandBase):
-
     def __init__(self, host):
         super(NodePurge, self).__init__(
             host,
@@ -189,12 +188,14 @@
         if not self.args.force:
             if not self.args.service:
                 message = _(
-                    f"Are you sure to purge PEP node [{self.args.node}]? This will "
-                    f"delete ALL items from it!")
+                    "Are you sure to purge PEP node [{node}]? This will "
+                    "delete ALL items from it!"
+                ).format(node=self.args.node)
             else:
                 message = _(
-                    f"Are you sure to delete node [{self.args.node}] on service "
-                    f"[{self.args.service}]? This will delete ALL items from it!")
+                    "Are you sure to delete node [{node}] on service "
+                    "[{service}]? This will delete ALL items from it!"
+                ).format(node=self.args.node, service=self.args.service)
             await self.host.confirmOrQuit(message, _("node purge cancelled"))
 
         try:
@@ -204,10 +205,10 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(msg=_(f"can't purge node: {e}"), error=True)
+            self.disp(msg=_("can't purge node: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"node [{self.args.node}] purged successfully"))
+            self.disp(_("node [{node}] purged successfully").format(node=self.args.node))
             self.host.quit()
 
 
@@ -233,10 +234,13 @@
     async def start(self):
         if not self.args.force:
             if not self.args.service:
-                message = _(f"Are you sure to delete PEP node [{self.args.node}] ?")
+                message = _("Are you sure to delete PEP node [{node}] ?").format(
+                    node=self.args.node
+                )
             else:
-                message = _(f"Are you sure to delete node [{self.args.node}] on "
-                            f"service [{self.args.service}]?")
+                message = _(
+                    "Are you sure to delete node [{node}] on " "service [{service}]?"
+                ).format(node=self.args.node, service=self.args.service)
             await self.host.confirmOrQuit(message, _("node deletion cancelled"))
 
         try:
@@ -249,7 +253,7 @@
             self.disp(f"can't delete node: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"node [{self.args.node}] deleted successfully"))
+            self.disp(_("node [{node}] deleted successfully").format(node=self.args.node))
             self.host.quit()
 
 
@@ -307,7 +311,6 @@
 
 
 class NodeImport(base.CommandBase):
-
     def __init__(self, host):
         super(NodeImport, self).__init__(
             host,
@@ -326,16 +329,20 @@
         self.parser.add_argument(
             "import_file",
             type=argparse.FileType(),
-            help=_("path to the XML file with data to import. The file must contain "
-                   "whole XML of each item to import."),
+            help=_(
+                "path to the XML file with data to import. The file must contain "
+                "whole XML of each item to import."
+            ),
         )
 
     async def start(self):
         try:
-            element, etree = xml_tools.etreeParse(self, self.args.import_file,
-                                                  reraise=True)
+            element, etree = xml_tools.etreeParse(
+                self, self.args.import_file, reraise=True
+            )
         except Exception as e:
             from lxml.etree import XMLSyntaxError
+
             if isinstance(e, XMLSyntaxError) and e.code == 5:
                 # we have extra content, this probaby means that item are not wrapped
                 # so we wrap them here and try again
@@ -343,14 +350,15 @@
                 xml_buf = "<import>" + self.args.import_file.read() + "</import>"
                 element, etree = xml_tools.etreeParse(self, xml_buf)
 
-        # we reverse element as we expect to have most recently published element first
-        # TODO: make this more explicit and add an option
+                # we reverse element as we expect to have most recently published element first
+                # TODO: make this more explicit and add an option
         element[:] = reversed(element)
 
-        if not all([i.tag == '{http://jabber.org/protocol/pubsub}item' for i in element]):
+        if not all([i.tag == "{http://jabber.org/protocol/pubsub}item" for i in element]):
             self.disp(
                 _("You are not using list of pubsub items, we can't import this file"),
-                error=True)
+                error=True,
+            )
             self.host.quit(C.EXIT_DATA_ERROR)
             return
 
@@ -358,8 +366,12 @@
         if self.args.admin:
             method = self.host.bridge.psAdminItemsSend
         else:
-            self.disp(_("Items are imported without using admin mode, publisher can't "
-                        "be changed"))
+            self.disp(
+                _(
+                    "Items are imported without using admin mode, publisher can't "
+                    "be changed"
+                )
+            )
             method = self.host.bridge.psItemsSend
 
         try:
@@ -375,10 +387,13 @@
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             if items_ids:
-                self.disp(_('items published with id(s) {items_ids}').format(
-                    items_ids=', '.join(items_ids)))
+                self.disp(
+                    _("items published with id(s) {items_ids}").format(
+                        items_ids=", ".join(items_ids)
+                    )
+                )
             else:
-                self.disp(_('items published'))
+                self.disp(_("items published"))
             self.host.quit()
 
 
@@ -645,8 +660,9 @@
         try:
             from lxml import etree
         except ImportError:
-            self.disp('lxml module must be installed to use edit, please install it '
-                      'with "pip install lxml"',
+            self.disp(
+                "lxml module must be installed to use edit, please install it "
+                'with "pip install lxml"',
                 error=True,
             )
             self.host.quit(1)
@@ -659,7 +675,9 @@
                 etree.tostring(schema_elt, encoding="utf-8", pretty_print=True)
             )
             content_file_obj.seek(0)
-        await self.runEditor("pubsub_schema_editor_args", content_file_path, content_file_obj)
+        await self.runEditor(
+            "pubsub_schema_editor_args", content_file_path, content_file_obj
+        )
 
     async def start(self):
         try:
@@ -669,7 +687,7 @@
                 self.profile,
             )
         except BridgeException as e:
-            if e.condition == 'item-not-found' or e.classname=="NotFound":
+            if e.condition == "item-not-found" or e.classname == "NotFound":
                 schema = ""
             else:
                 self.disp(f"can't edit schema: {e}", error=True)
@@ -702,7 +720,7 @@
                 self.profile,
             )
         except BridgeException as e:
-            if e.condition == 'item-not-found' or e.classname=="NotFound":
+            if e.condition == "item-not-found" or e.classname == "NotFound":
                 schema = None
             else:
                 self.disp(f"can't get schema: {e}", error=True)
@@ -772,7 +790,7 @@
         extra = {}
         publish_options = NodeCreate.get_config_options(self.args)
         if publish_options:
-            extra['publish_options'] = publish_options
+            extra["publish_options"] = publish_options
 
         try:
             published_id = await self.host.bridge.psItemSend(
@@ -784,12 +802,12 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't send item: {e}"), error=True)
+            self.disp(_("can't send item: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             if published_id:
                 if self.args.quiet:
-                    self.disp(published_id, end='')
+                    self.disp(published_id, end="")
                 else:
                     self.disp(f"Item published at {published_id}")
             else:
@@ -833,10 +851,10 @@
                 )
             )
         except BridgeException as e:
-            if e.condition == 'item-not-found' or e.classname=="NotFound":
+            if e.condition == "item-not-found" or e.classname == "NotFound":
                 self.disp(
                     f"The node {self.args.node} doesn't exist on {self.args.service}",
-                    error=True
+                    error=True,
                 )
                 self.host.quit(C.EXIT_NOT_FOUND)
             else:
@@ -846,7 +864,7 @@
             self.disp(f"Internal error: {e}", error=True)
             self.host.quit(C.EXIT_INTERNAL_ERROR)
         else:
-            await self.output(ps_result['items'])
+            await self.output(ps_result["items"])
             self.host.quit(C.EXIT_OK)
 
 
@@ -886,10 +904,10 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't delete item: {e}"), error=True)
+            self.disp(_("can't delete item: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"item {self.args.item} has been deleted"))
+            self.disp(_("item {item} has been deleted").format(item=self.args.item))
             self.host.quit(C.EXIT_OK)
 
 
@@ -928,8 +946,9 @@
         try:
             from lxml import etree
         except ImportError:
-            self.disp('lxml module must be installed to use edit, please install it '
-                      'with "pip install lxml"',
+            self.disp(
+                "lxml module must be installed to use edit, please install it "
+                'with "pip install lxml"',
                 error=True,
             )
             self.host.quit(1)
@@ -939,7 +958,7 @@
                 service, node, 1, items, "", {}, self.profile
             )
         )
-        item_raw = ps_result['items'][0]
+        item_raw = ps_result["items"][0]
         parser = etree.XMLParser(remove_blank_text=True, recover=True)
         item_elt = etree.fromstring(item_raw, parser)
         item_id = item_elt.get("id")
@@ -951,17 +970,18 @@
         return etree.tostring(payload, encoding="unicode", pretty_print=True), item_id
 
     async def start(self):
-        (self.pubsub_service,
-         self.pubsub_node,
-         self.pubsub_item,
-         content_file_path,
-         content_file_obj) = await self.getItemPath()
+        (
+            self.pubsub_service,
+            self.pubsub_node,
+            self.pubsub_item,
+            content_file_path,
+            content_file_obj,
+        ) = await self.getItemPath()
         await self.runEditor("pubsub_editor_args", content_file_path, content_file_obj)
         self.host.quit()
 
 
 class Rename(base.CommandBase):
-
     def __init__(self, host):
         base.CommandBase.__init__(
             self,
@@ -973,10 +993,7 @@
         )
 
     def add_parser_options(self):
-        self.parser.add_argument(
-            "new_id",
-            help=_("new item id to use")
-        )
+        self.parser.add_argument("new_id", help=_("new item id to use"))
 
     async def start(self):
         try:
@@ -988,9 +1005,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(
-                f"can't rename item: {e}", error=True
-            )
+            self.disp(f"can't rename item: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp("Item renamed")
@@ -1021,7 +1036,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't subscribe to node: {e}"), error=True)
+            self.disp(_("can't subscribe to node: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp(_("subscription done"), 1)
@@ -1055,7 +1070,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't unsubscribe from node: {e}"), error=True)
+            self.disp(_("can't unsubscribe from node: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             self.disp(_("subscription removed"), 1)
@@ -1084,7 +1099,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(_(f"can't retrieve subscriptions: {e}"), error=True)
+            self.disp(_("can't retrieve subscriptions: {e}").format(e=e), error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             await self.output(subscriptions)
@@ -1113,9 +1128,7 @@
                 self.profile,
             )
         except Exception as e:
-            self.disp(
-                f"can't get node affiliations: {e}", error=True
-            )
+            self.disp(f"can't get node affiliations: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             await self.output(affiliations)
@@ -1169,16 +1182,20 @@
             "--max-depth",
             type=int,
             default=0,
-            help=_("maximum depth of recursion (will search linked nodes if > 0, "
-                   "DEFAULT: 0)"),
+            help=_(
+                "maximum depth of recursion (will search linked nodes if > 0, "
+                "DEFAULT: 0)"
+            ),
         )
         self.parser.add_argument(
             "-M",
             "--node-max",
             type=int,
             default=30,
-            help=_("maximum number of items to get per node ({} to get all items, "
-                   "DEFAULT: 30)".format( C.NO_LIMIT)),
+            help=_(
+                "maximum number of items to get per node ({} to get all items, "
+                "DEFAULT: 30)".format(C.NO_LIMIT)
+            ),
         )
         self.parser.add_argument(
             "-N",
@@ -1233,9 +1250,10 @@
             dest="filters",
             type=filter_python,
             metavar="PYTHON_CODE",
-            help=_('Python expression which much return a bool (True to keep item, '
-                   'False to reject it). "item" is raw text item, "item_xml" is '
-                   'lxml\'s etree.Element'
+            help=_(
+                "Python expression which much return a bool (True to keep item, "
+                'False to reject it). "item" is raw text item, "item_xml" is '
+                "lxml's etree.Element"
             ),
         )
 
@@ -1362,8 +1380,10 @@
             return self.etree.fromstring(item)
         except self.etree.XMLSyntaxError:
             self.disp(
-                _("item doesn't looks like XML, you have probably used --only-matching "
-                  "somewhere before and we have no more XML"),
+                _(
+                    "item doesn't looks like XML, you have probably used --only-matching "
+                    "somewhere before and we have no more XML"
+                ),
                 error=True,
             )
             self.host.quit(C.EXIT_BAD_ARG)
@@ -1397,9 +1417,7 @@
                     # doesn't really make sens to keep a fixed string
                     # so we raise an error
                     self.host.disp(
-                        _(
-                            "--only-matching used with fixed --text string, are you sure?"
-                        ),
+                        _("--only-matching used with fixed --text string, are you sure?"),
                         error=True,
                     )
                     self.host.quit(C.EXIT_BAD_ARG)
@@ -1420,9 +1438,7 @@
                 try:
                     elts = item_xml.xpath(value, namespaces=self.args.namespace)
                 except self.etree.XPathEvalError as e:
-                    self.disp(
-                        _("can't use xpath: {reason}").format(reason=e), error=True
-                    )
+                    self.disp(_("can't use xpath: {reason}").format(reason=e), error=True)
                     self.host.quit(C.EXIT_BAD_ARG)
                 keep = bool(elts)
                 if keep and only_matching:
@@ -1436,18 +1452,14 @@
             elif type_ == "python":
                 if item_xml is None:
                     item_xml = self.parseXml(item)
-                cmd_ns = {
-                    "etree": self.etree,
-                    "item": item,
-                    "item_xml": item_xml
-                    }
+                cmd_ns = {"etree": self.etree, "item": item, "item_xml": item_xml}
                 try:
                     keep = eval(value, cmd_ns)
                 except SyntaxError as e:
                     self.disp(str(e), error=True)
                     self.host.quit(C.EXIT_BAD_ARG)
 
-            ## flags
+                    ## flags
 
             elif type_ == "ignore-case":
                 ignore_case = value
@@ -1512,15 +1524,14 @@
                 p = await asyncio.create_subprocess_exec(*cmd_args)
                 ret = await p.wait()
             else:
-                p = await asyncio.create_subprocess_exec(*cmd_args,
-                                                         stdin=subprocess.PIPE)
+                p = await asyncio.create_subprocess_exec(*cmd_args, stdin=subprocess.PIPE)
                 await p.communicate(item.encode(sys.getfilesystemencoding()))
                 ret = p.returncode
             if ret != 0:
                 self.disp(
                     A.color(
                         C.A_FAILURE,
-                        _(f"executed command failed with exit code {ret}"),
+                        _("executed command failed with exit code {ret}").format(ret=ret),
                     )
                 )
 
@@ -1533,7 +1544,7 @@
         @param depth(int): current depth level
             0 for first node, 1 for first children, and so on
         """
-        for item in ps_result['items']:
+        for item in ps_result["items"]:
             if depth < self.args.max_depth:
                 await self.getSubNodes(item, depth)
             keep, item = self.filter(item)
@@ -1541,7 +1552,7 @@
                 continue
             await self.doItemAction(item, ps_result)
 
-        #  we check if we got all getItems results
+            #  we check if we got all getItems results
         self.to_get -= 1
         if self.to_get == 0:
             # yes, we can quit
@@ -1562,8 +1573,8 @@
         if not self.args.node:
             # TODO: handle get service affiliations when node is not set
             self.parser.error(_("empty node is not handled yet"))
-        # to_get is increased on each get and decreased on each answer
-        # when it reach 0 again, the command is finished
+            # to_get is increased on each get and decreased on each answer
+            # when it reach 0 again, the command is finished
         self.to_get = 0
         self._etree = None
         if self.args.filters is None:
@@ -1601,27 +1612,33 @@
             "--ignore-errors",
             action="store_true",
             help=_(
-                "if command return a non zero exit code, ignore the item and continue"),
+                "if command return a non zero exit code, ignore the item and continue"
+            ),
         )
         self.parser.add_argument(
             "-A",
             "--all",
             action="store_true",
-            help=_("get all items by looping over all pages using RSM")
+            help=_("get all items by looping over all pages using RSM"),
         )
         self.parser.add_argument(
             "command_path",
-            help=_("path to the command to use. Will be called repetitivly with an "
-                   "item as input. Output (full item XML) will be used as new one. "
-                   'Return "DELETE" string to delete the item, and "SKIP" to ignore it'),
+            help=_(
+                "path to the command to use. Will be called repetitivly with an "
+                "item as input. Output (full item XML) will be used as new one. "
+                'Return "DELETE" string to delete the item, and "SKIP" to ignore it'
+            ),
         )
 
     async def psItemsSendCb(self, item_ids, metadata):
         if item_ids:
-            self.disp(_('items published with ids {item_ids}').format(
-                item_ids=', '.join(item_ids)))
+            self.disp(
+                _("items published with ids {item_ids}").format(
+                    item_ids=", ".join(item_ids)
+                )
+            )
         else:
-            self.disp(_('items published'))
+            self.disp(_("items published"))
         if self.args.all:
             return await self.handleNextPage(metadata)
         else:
@@ -1634,30 +1651,34 @@
         @param metadata(dict): metadata as returned by psItemsGet
         """
         try:
-            last = metadata['rsm']['last']
-            index = int(metadata['rsm']['index'])
-            count = int(metadata['rsm']['count'])
+            last = metadata["rsm"]["last"]
+            index = int(metadata["rsm"]["index"])
+            count = int(metadata["rsm"]["count"])
         except KeyError:
-            self.disp(_("Can't retrieve all items, RSM metadata not available"),
-                      error=True)
+            self.disp(
+                _("Can't retrieve all items, RSM metadata not available"), error=True
+            )
             self.host.quit(C.EXIT_MISSING_FEATURE)
         except ValueError as e:
-            self.disp(_("Can't retrieve all items, bad RSM metadata: {msg}")
-                      .format(msg=e), error=True)
+            self.disp(
+                _("Can't retrieve all items, bad RSM metadata: {msg}").format(msg=e),
+                error=True,
+            )
             self.host.quit(C.EXIT_ERROR)
 
         if index + self.args.rsm_max >= count:
-            self.disp(_('All items transformed'))
+            self.disp(_("All items transformed"))
             self.host.quit(0)
 
-        self.disp(_('Retrieving next page ({page_idx}/{page_total})').format(
-            page_idx = int(index/self.args.rsm_max) + 1,
-            page_total = int(count/self.args.rsm_max),
+        self.disp(
+            _("Retrieving next page ({page_idx}/{page_total})").format(
+                page_idx=int(index / self.args.rsm_max) + 1,
+                page_total=int(count / self.args.rsm_max),
             )
         )
 
         extra = self.getPubsubExtra()
-        extra['rsm_after'] = last
+        extra["rsm_after"] = last
         try:
             ps_result = await data_format.deserialise(
                 self.host.bridge.psItemsGet(
@@ -1671,36 +1692,36 @@
                 )
             )
         except Exception as e:
-            self.disp(
-                f"can't retrieve items: {e}", error=True
-            )
+            self.disp(f"can't retrieve items: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
             await self.psItemsGetCb(ps_result)
 
     async def psItemsGetCb(self, ps_result):
-        encoding = 'utf-8'
+        encoding = "utf-8"
         new_items = []
 
-        for item in ps_result['items']:
+        for item in ps_result["items"]:
             if self.check_duplicates:
                 # this is used when we are not ordering by creation
                 # to avoid infinite loop
                 item_elt, __ = xml_tools.etreeParse(self, item)
-                item_id = item_elt.get('id')
+                item_id = item_elt.get("id")
                 if item_id in self.items_ids:
-                    self.disp(_(
-                        "Duplicate found on item {item_id}, we have probably handled "
-                        "all items.").format(item_id=item_id))
+                    self.disp(
+                        _(
+                            "Duplicate found on item {item_id}, we have probably handled "
+                            "all items."
+                        ).format(item_id=item_id)
+                    )
                     self.host.quit()
                 self.items_ids.append(item_id)
 
-            # we launch the command to filter the item
+                # we launch the command to filter the item
             try:
                 p = await asyncio.create_subprocess_exec(
-                    self.args.command_path,
-                    stdin=subprocess.PIPE,
-                    stdout=subprocess.PIPE)
+                    self.args.command_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE
+                )
             except OSError as e:
                 exit_code = C.EXIT_CMD_NOT_FOUND if e.errno == 2 else C.EXIT_ERROR
                 self.disp(f"Can't execute the command: {e}", error=True)
@@ -1709,20 +1730,23 @@
             cmd_std_out, cmd_std_err = await p.communicate(item.encode(encoding))
             ret = p.returncode
             if ret != 0:
-                self.disp(f"The command returned a non zero status while parsing the "
-                          f"following item:\n\n{item}", error=True)
+                self.disp(
+                    f"The command returned a non zero status while parsing the "
+                    f"following item:\n\n{item}",
+                    error=True,
+                )
                 if self.args.ignore_errors:
                     continue
                 else:
                     self.host.quit(C.EXIT_CMD_ERROR)
             if cmd_std_err is not None:
-                cmd_std_err = cmd_std_err.decode(encoding, errors='ignore')
+                cmd_std_err = cmd_std_err.decode(encoding, errors="ignore")
                 self.disp(cmd_std_err, error=True)
             cmd_std_out = cmd_std_out.decode(encoding).strip()
             if cmd_std_out == "DELETE":
                 item_elt, __ = xml_tools.etreeParse(self, item)
-                item_id = item_elt.get('id')
-                self.disp(_(f"Deleting item {item_id}"))
+                item_id = item_elt.get("id")
+                self.disp(_("Deleting item {item_id}").format(item_id=item_id))
                 if self.args.apply:
                     try:
                         await self.host.bridge.psItemRetract(
@@ -1733,28 +1757,31 @@
                             self.profile,
                         )
                     except Exception as e:
-                        self.disp(
-                            f"can't delete item {item_id}: {e}", error=True
-                        )
+                        self.disp(f"can't delete item {item_id}: {e}", error=True)
                         self.host.quit(C.EXIT_BRIDGE_ERRBACK)
                 continue
             elif cmd_std_out == "SKIP":
                 item_elt, __ = xml_tools.etreeParse(self, item)
-                item_id = item_elt.get('id')
+                item_id = item_elt.get("id")
                 self.disp(_("Skipping item {item_id}").format(item_id=item_id))
                 continue
             element, etree = xml_tools.etreeParse(self, cmd_std_out)
 
             # at this point command has been run and we have a etree.Element object
             if element.tag not in ("item", "{http://jabber.org/protocol/pubsub}item"):
-                self.disp("your script must return a whole item, this is not:\n{xml}"
-                    .format(xml=etree.tostring(element, encoding="unicode")), error=True)
+                self.disp(
+                    "your script must return a whole item, this is not:\n{xml}".format(
+                        xml=etree.tostring(element, encoding="unicode")
+                    ),
+                    error=True,
+                )
                 self.host.quit(C.EXIT_DATA_ERROR)
 
             if not self.args.apply:
                 # we have a dry run, we just display filtered items
-                serialised = etree.tostring(element, encoding='unicode',
-                                            pretty_print=True)
+                serialised = etree.tostring(
+                    element, encoding="unicode", pretty_print=True
+                )
                 self.disp(serialised)
             else:
                 new_items.append(etree.tostring(element, encoding="unicode"))
@@ -1788,13 +1815,17 @@
         if self.args.all and self.args.order_by != C.ORDER_BY_CREATION:
             self.check_duplicates = True
             self.items_ids = []
-            self.disp(A.color(
-                A.FG_RED, A.BOLD,
-                '/!\\ "--all" should be used with "--order-by creation" /!\\\n',
-                A.RESET,
-                "We'll update items, so order may change during transformation,\n"
-                "we'll try to mitigate that by stopping on first duplicate,\n"
-                "but this method is not safe, and some items may be missed.\n---\n"))
+            self.disp(
+                A.color(
+                    A.FG_RED,
+                    A.BOLD,
+                    '/!\\ "--all" should be used with "--order-by creation" /!\\\n',
+                    A.RESET,
+                    "We'll update items, so order may change during transformation,\n"
+                    "we'll try to mitigate that by stopping on first duplicate,\n"
+                    "but this method is not safe, and some items may be missed.\n---\n",
+                )
+            )
         else:
             self.check_duplicates = False
 
@@ -1855,9 +1886,7 @@
         if not self.args.service:
             try:
                 jid_ = await self.host.bridge.asyncGetParamA(
-                    "JabberID",
-                    "Connection",
-                    profile_key=self.args.profile
+                    "JabberID", "Connection", profile_key=self.args.profile
                 )
             except Exception as e:
                 self.disp(f"can't retrieve jid: {e}", error=True)
@@ -1968,7 +1997,9 @@
             self.disp(f"can't delete hook: {e}", error=True)
             self.host.quit(C.EXIT_BRIDGE_ERRBACK)
         else:
-            self.disp(_(f"{nb_deleted} hook(s) have been deleted"))
+            self.disp(
+                _("{nb_deleted} hook(s) have been deleted").format(nb_deleted=nb_deleted)
+            )
             self.host.quit()
 
 
--- a/sat_frontends/jp/common.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/common.py	Mon Jun 14 18:35:12 2021 +0200
@@ -143,12 +143,11 @@
             raise OSError("path must link to a regular file")
         if path.parent != getTmpDir(self.sat_conf, self.cat_dir):
             self.disp(
-                f"File {path} is not in SàT temporary hierarchy, we do not remove "
-                f"it",
+                f"File {path} is not in SàT temporary hierarchy, we do not remove " f"it",
                 2,
             )
             return
-        # we have 2 files per draft with use_metadata, so we double max
+            # we have 2 files per draft with use_metadata, so we double max
         unlink_max = SECURE_UNLINK_MAX * 2 if self.use_metadata else SECURE_UNLINK_MAX
         backup_dir = getTmpDir(self.sat_conf, self.cat_dir, SECURE_UNLINK_DIR)
         if not os.path.exists(backup_dir):
@@ -157,9 +156,7 @@
         backup_path = os.path.join(backup_dir, filename)
         # we move file to backup dir
         self.host.disp(
-            "Backuping file {src} to {dst}".format(
-                src=path, dst=backup_path
-            ),
+            "Backuping file {src} to {dst}".format(src=path, dst=backup_path),
             1,
         )
         os.rename(path, backup_path)
@@ -171,8 +168,14 @@
                 self.host.disp("Purging backup file {}".format(path), 2)
                 os.unlink(path)
 
-    async def runEditor(self, editor_args_opt, content_file_path, content_file_obj,
-                        meta_file_path=None, meta_ori=None):
+    async def runEditor(
+        self,
+        editor_args_opt,
+        content_file_path,
+        content_file_obj,
+        meta_file_path=None,
+        meta_ori=None,
+    ):
         """Run editor to edit content and metadata
 
         @param editor_args_opt(unicode): option in [jp] section in configuration for
@@ -188,7 +191,7 @@
             assert meta_file_path is None
             assert meta_ori is None
 
-        # we calculate hashes to check for modifications
+            # we calculate hashes to check for modifications
         import hashlib
 
         content_file_obj.seek(0)
@@ -217,9 +220,10 @@
         if not args:
             args = [content_file_path]
 
-        # actual editing
+            # actual editing
         editor_process = await asyncio.create_subprocess_exec(
-            editor, *[str(a) for a in args])
+            editor, *[str(a) for a in args]
+        )
         editor_exit = await editor_process.wait()
 
         # edition will now be checked, and data will be sent if it was a success
@@ -243,7 +247,7 @@
                 )
                 self.host.quit(C.EXIT_NOT_FOUND)
 
-            # metadata
+                # metadata
             if self.use_metadata:
                 try:
                     with meta_file_path.open("rb") as f:
@@ -268,8 +272,8 @@
             if self.use_metadata and not metadata.get("publish", True):
                 self.disp(
                     f'Publication blocked by "publish" key in metadata, cancelling '
-                    f'edition.\n\ntemporary file path:\t{content_file_path}\nmetadata '
-                    f'file path:\t{meta_file_path}',
+                    f"edition.\n\ntemporary file path:\t{content_file_path}\nmetadata "
+                    f"file path:\t{meta_file_path}",
                     error=True,
                 )
                 self.host.quit()
@@ -289,7 +293,7 @@
                     os.unlink(meta_file_path)
                 self.host.quit()
 
-            # time to re-check the hash
+                # time to re-check the hash
             elif tmp_ori_hash == hashlib.sha1(content).digest() and (
                 not self.use_metadata or meta_ori == metadata
             ):
@@ -354,9 +358,7 @@
             )
             return os.fdopen(fd, "w+b"), Path(path)
         except OSError as e:
-            self.disp(
-                f"Can't create temporary file: {e}", error=True
-            )
+            self.disp(f"Can't create temporary file: {e}", error=True)
             self.host.quit(1)
 
     def getCurrentFile(self, profile):
@@ -371,7 +373,7 @@
         tmp_dir = getTmpDir(self.sat_conf, self.cat_dir, profile)
         available = [
             p
-            for p in tmp_dir.glob(f'{self.cat_dir}_*')
+            for p in tmp_dir.glob(f"{self.cat_dir}_*")
             if not p.match(f"*{METADATA_SUFF}")
         ]
         if not available:
@@ -436,9 +438,9 @@
                     else:
                         self.disp(
                             _(
-                                f'item "{item}" not found, we create a new item with'
-                                f'this id'
-                            ),
+                                'item "{item}" not found, we create a new item with'
+                                "this id"
+                            ).format(item=item),
                             2,
                         )
                     content_file_obj.seek(0)
@@ -453,9 +455,7 @@
                     content_file_obj.write("\n*****\n")
                 content_file_obj.write(content.encode("utf-8"))
                 content_file_obj.seek(0)
-                self.disp(
-                    _(f'item "{item}" found, we edit it'), 2
-                )
+                self.disp(_('item "{item}" found, we edit it').format(item=item), 2)
         else:
             self.disp("Editing a new item", 2)
             if self.use_metadata:
@@ -498,7 +498,7 @@
         size = None
         if headers:
             # we use a namedtuple to make the value easily accessible from filters
-            headers_safe = [re.sub(r'[^a-zA-Z_]', '_', h) for h in headers]
+            headers_safe = [re.sub(r"[^a-zA-Z_]", "_", h) for h in headers]
             row_cls = namedtuple("RowData", headers_safe)
         else:
             row_cls = tuple
@@ -516,8 +516,8 @@
                             col_value = filter_(value, row_cls(*row_data_list))
                         except TypeError:
                             col_value = filter_(value)
-                    # we count size without ANSI code as they will change length of the
-                    # string when it's mostly style/color changes.
+                            # we count size without ANSI code as they will change length of the
+                            # string when it's mostly style/color changes.
                     col_size = len(regex.ansiRemove(col_value))
                 else:
                     col_value = str(value)
@@ -562,7 +562,8 @@
 
     @classmethod
     def fromListDict(
-        cls, host, data, keys=None, headers=None, filters=None, defaults=None):
+        cls, host, data, keys=None, headers=None, filters=None, defaults=None
+    ):
         """Create a table from a list of dictionaries
 
         each dictionary is a row of the table, keys being columns names.
@@ -694,13 +695,13 @@
             bottom_sep = col_sep_size * bottom
         if not show_borders:
             left = right = head_line_left = head_line_right = ""
-        # top border
+            # top border
         if show_borders:
             self._disp(
                 top_left + top_sep.join([top * size for size in sizes]) + top_right
             )
 
-        # headers
+            # headers
         if show_header and self.headers is not None:
             self._disp(
                 left
@@ -714,7 +715,7 @@
                 + head_line_right
             )
 
-        # content
+            # content
         if columns_alignment == "left":
             alignment = lambda idx, s: ansi_ljust(s, sizes[idx])
         elif columns_alignment == "center":
@@ -740,7 +741,7 @@
                 + bottom_sep.join([bottom * size for size in sizes])
                 + bottom_right
             )
-        #  we return self so string can be used after display (table.display().string)
+            #  we return self so string can be used after display (table.display().string)
         return self
 
     def display_blank(self, **kwargs):
@@ -781,8 +782,10 @@
         uri_data = uris_data[key]
     except KeyError:
         host.disp(
-            _(f"No {key} URI specified for this project, please specify service and "
-              f"node"),
+            _(
+                "No {key} URI specified for this project, please specify service and "
+                "node"
+            ).format(key=key),
             error=True,
         )
         host.quit(C.EXIT_NOT_FOUND)
@@ -790,7 +793,7 @@
     uri = uri_data["uri"]
 
     # set extra metadata if they are specified
-    for data_key in ['labels']:
+    for data_key in ["labels"]:
         new_values_json = uri_data.get(data_key)
         if uri_data is not None:
             if meta_map is None:
@@ -803,7 +806,7 @@
             try:
                 values = getattr(args, data_key)
             except AttributeError:
-                raise exceptions.InternalError(f'there is no {data_key!r} arguments')
+                raise exceptions.InternalError(f"there is no {data_key!r} arguments")
             else:
                 if values is None:
                     values = []
@@ -815,5 +818,5 @@
         args.service = parsed_uri["path"]
         args.node = parsed_uri["node"]
     except KeyError:
-        host.disp(_(f"Invalid URI found: {uri}"), error=True)
+        host.disp(_("Invalid URI found: {uri}").format(uri=uri), error=True)
         host.quit(C.EXIT_DATA_ERROR)
--- a/sat_frontends/jp/xmlui_manager.py	Mon Jun 14 12:19:21 2021 +0200
+++ b/sat_frontends/jp/xmlui_manager.py	Mon Jun 14 18:35:12 2021 +0200
@@ -174,12 +174,11 @@
 
 
 class EmptyWidget(xmlui_base.EmptyWidget, Widget):
-
     def __init__(self, xmlui_parent):
         Widget.__init__(self, xmlui_parent)
 
     async def show(self):
-        self.host.disp('')
+        self.host.disp("")
 
 
 class TextWidget(xmlui_base.TextWidget, ValueWidget):
@@ -199,7 +198,7 @@
         except AttributeError:
             return None
 
-    async def show(self, end='\n', ansi=""):
+    async def show(self, end="\n", ansi=""):
         """show label
 
         @param end(str): same as for [JP.disp]
@@ -211,6 +210,7 @@
 class JidWidget(xmlui_base.JidWidget, TextWidget):
     type = "jid"
 
+
 class StringWidget(xmlui_base.StringWidget, InputWidget):
     type = "string"
 
@@ -221,7 +221,7 @@
             elems = []
             self.verboseName(elems)
             if self.value:
-                elems.append(_(f"(enter: {self.value})"))
+                elems.append(_("(enter: {value})").format(value=self.value))
             elems.extend([C.A_HEADER, "> "])
             value = await self.host.ainput(A.color(*elems))
             if value:
@@ -244,21 +244,24 @@
             self.disp(self.value)
         else:
             if self.value:
-                self.disp(A.color(C.A_HEADER, "↓ current value ↓\n", A.FG_CYAN, self.value,
-                                  ""))
+                self.disp(
+                    A.color(C.A_HEADER, "↓ current value ↓\n", A.FG_CYAN, self.value, "")
+                )
 
             values = []
             while True:
                 try:
                     if not values:
-                        line = await self.host.ainput(A.color(C.A_HEADER, "[Ctrl-D to finish]> "))
+                        line = await self.host.ainput(
+                            A.color(C.A_HEADER, "[Ctrl-D to finish]> ")
+                        )
                     else:
                         line = await self.host.ainput()
                     values.append(line)
                 except EOFError:
-                   break
+                    break
 
-            self.value = '\n'.join(values).rstrip()
+            self.value = "\n".join(values).rstrip()
 
 
 class XHTMLBoxWidget(xmlui_base.XHTMLBoxWidget, StringWidget):
@@ -269,7 +272,8 @@
         #        this only for now to make it simpler, it must be refactored
         #        to use async when jp will be fully async (expected for 0.8)
         self.value = await self.host.bridge.syntaxConvert(
-            self.value, C.SYNTAX_XHTML, "markdown", False, self.host.profile)
+            self.value, C.SYNTAX_XHTML, "markdown", False, self.host.profile
+        )
         await super(XHTMLBoxWidget, self).show()
 
 
@@ -285,7 +289,7 @@
         if not self.options:
             return
 
-        # list display
+            # list display
         self.verboseName()
 
         for idx, (value, label) in enumerate(self.options):
@@ -304,12 +308,15 @@
             self.value = self.options[0][0]
             return
 
-        #  we ask use to choose an option
+            #  we ask use to choose an option
         choice = None
         limit_max = len(self.options) - 1
         while choice is None or choice < 0 or choice > limit_max:
             choice = await self.host.ainput(
-                A.color(C.A_HEADER, _(f"your choice (0-{limit_max}): "))
+                A.color(
+                    C.A_HEADER,
+                    _("your choice (0-{limit_max}): ").format(limit_max=limit_max),
+                )
             )
             try:
                 choice = int(choice)
@@ -328,12 +335,14 @@
         if self.read_only or self.root.read_only:
             self.disp(disp_true if self.value else disp_false)
         else:
-            self.disp(A.color(C.A_HEADER, "0: ",
-                              disp_false, A.RESET,
-                              " *" if not self.value else ""))
-            self.disp(A.color(C.A_HEADER, "1: ",
-                              disp_true, A.RESET,
-                              " *" if self.value else ""))
+            self.disp(
+                A.color(
+                    C.A_HEADER, "0: ", disp_false, A.RESET, " *" if not self.value else ""
+                )
+            )
+            self.disp(
+                A.color(C.A_HEADER, "1: ", disp_true, A.RESET, " *" if self.value else "")
+            )
             choice = None
             while choice not in ("0", "1"):
                 elems = [C.A_HEADER, _("your choice (0,1): ")]
@@ -345,8 +354,7 @@
     def _xmluiGetValue(self):
         return C.boolConst(self.value)
 
-
-## Containers ##
+        ## Containers ##
 
 
 class Container(Base):
@@ -383,7 +391,7 @@
 
     async def show(self):
         for child in self.children:
-            end = '\n'
+            end = "\n"
             # we check linked widget type
             # to see if we want the label on the same line or not
             if child.type == "label":
@@ -396,15 +404,14 @@
                         "string",
                         "jid_input",
                     ):
-                        end = ' '
+                        end = " "
                     elif wid_type == "bool" and for_widget.read_only:
-                        end = ' '
+                        end = " "
                 await child.show(end=end, ansi=A.FG_CYAN)
             else:
                 await child.show()
 
-
-## Dialogs ##
+                ## Dialogs ##
 
 
 class Dialog(object):
@@ -422,8 +429,8 @@
         """
         raise NotImplementedError(self.__class__)
 
+
 class MessageDialog(xmlui_base.MessageDialog, Dialog):
-
     def __init__(self, xmlui_parent, title, message, level):
         Dialog.__init__(self, xmlui_parent)
         xmlui_base.MessageDialog.__init__(self, xmlui_parent)
@@ -437,7 +444,6 @@
 
 
 class NoteDialog(xmlui_base.NoteDialog, Dialog):
-
     def __init__(self, xmlui_parent, title, message, level):
         Dialog.__init__(self, xmlui_parent)
         xmlui_base.NoteDialog.__init__(self, xmlui_parent)
@@ -456,12 +462,15 @@
 
 
 class ConfirmDialog(xmlui_base.ConfirmDialog, Dialog):
-
     def __init__(self, xmlui_parent, title, message, level, buttons_set):
         Dialog.__init__(self, xmlui_parent)
         xmlui_base.ConfirmDialog.__init__(self, xmlui_parent)
         self.title, self.message, self.level, self.buttons_set = (
-            title, message, level, buttons_set)
+            title,
+            message,
+            level,
+            buttons_set,
+        )
 
     async def show(self):
         # TODO: handle buttons_set and level
@@ -469,16 +478,15 @@
         if self.title:
             self.disp(A.color(C.A_HEADER, self.title))
         input_ = None
-        while input_ not in ('y', 'n'):
+        while input_ not in ("y", "n"):
             input_ = await self.host.ainput(f"{self.message} (y/n)? ")
             input_ = input_.lower()
-        if input_ == 'y':
+        if input_ == "y":
             self._xmluiValidated()
         else:
             self._xmluiCancelled()
 
-
-## Factory ##
+            ## Factory ##
 
 
 class WidgetFactory(object):
@@ -496,8 +504,17 @@
     workflow = None
     _submit_cb = None
 
-    def __init__(self, host, parsed_dom, title=None, flags=None, callback=None,
-                 ignore=None, whitelist=None, profile=None):
+    def __init__(
+        self,
+        host,
+        parsed_dom,
+        title=None,
+        flags=None,
+        callback=None,
+        ignore=None,
+        whitelist=None,
+        profile=None,
+    ):
         xmlui_base.XMLUIPanel.__init__(
             self,
             host,
@@ -588,7 +605,7 @@
             await xmlui.show()
             if xmlui.submit_id:
                 await xmlui.onFormSubmitted()
-        # TODO: handle data other than XMLUI
+                # TODO: handle data other than XMLUI
         if not XMLUIPanel._actions:
             if self._submit_cb is None:
                 self.host.quit()
@@ -622,6 +639,7 @@
         pass
 
 
-create = partial(xmlui_base.create, class_map={
-    xmlui_base.CLASS_PANEL: XMLUIPanel,
-    xmlui_base.CLASS_DIALOG: XMLUIDialog})
+create = partial(
+    xmlui_base.create,
+    class_map={xmlui_base.CLASS_PANEL: XMLUIPanel, xmlui_base.CLASS_DIALOG: XMLUIDialog},
+)