diff sat_frontends/jp/cmd_message.py @ 3040:fee60f17ebac

jp: jp asyncio port: /!\ this commit is huge. Jp is temporarily not working with `dbus` bridge /!\ This patch implements the port of jp to asyncio, so it is now correctly using the bridge asynchronously, and it can be used with bridges like `pb`. This also simplify the code, notably for things which were previously implemented with many callbacks (like pagination with RSM). During the process, some behaviours have been modified/fixed, in jp and backends, check diff for details.
author Goffi <goffi@goffi.org>
date Wed, 25 Sep 2019 08:56:41 +0200
parents ab2696e34d29
children 9d0df638c8b4
line wrap: on
line diff
--- a/sat_frontends/jp/cmd_message.py	Wed Sep 25 08:53:38 2019 +0200
+++ b/sat_frontends/jp/cmd_message.py	Wed Sep 25 08:56:41 2019 +0200
@@ -25,7 +25,6 @@
 from sat.tools.utils import clean_ustr
 from sat.tools.common import data_format
 from sat.tools.common.ansi import ANSI as A
-from functools import partial
 
 __commands__ = ["Message"]
 
@@ -33,7 +32,6 @@
 class Send(base.CommandBase):
     def __init__(self, host):
         super(Send, self).__init__(host, "send", help=_("send a message to a contact"))
-        self.need_loop=True
 
     def add_parser_options(self):
         self.parser.add_argument(
@@ -84,25 +82,15 @@
             "jid", help=_("the destination jid")
         )
 
-    def multi_send_cb(self):
-        self.sent += 1
-        if self.sent == self.to_send:
-            self.host.quit(self.errcode)
-
-    def multi_send_eb(self, failure_, msg):
-        self.disp(_("Can't send message [{msg}]: {reason}").format(
-            msg=msg, reason=failure_))
-        self.errcode = C.EXIT_BRIDGE_ERRBACK
-        self.multi_send_cb()
-
-    def sendStdin(self, dest_jid):
+    async def sendStdin(self, dest_jid):
         """Send incomming data on stdin to jabber contact
 
         @param dest_jid: destination jid
         """
         header = "\n" if self.args.new_line else ""
+        # FIXME: stdin is not read asynchronously at the moment
         stdin_lines = [
-            stream.decode("utf-8", "ignore") for stream in sys.stdin.readlines()
+            stream for stream in sys.stdin.readlines()
         ]
         extra = {}
         if self.args.subject is None:
@@ -113,99 +101,98 @@
         if self.args.xhtml or self.args.rich:
             key = "xhtml" if self.args.xhtml else "rich"
             if self.args.lang:
-                key = "{}_{}".format(key, self.args.lang)
+                key = f"{key}_{self.args.lang}"
             extra[key] = clean_ustr("".join(stdin_lines))
             stdin_lines = []
 
-        if self.args.separate:  # we send stdin in several messages
-            self.to_send = 0
-            self.sent = 0
-            self.errcode = 0
+        to_send = []
+
+        error = False
 
+        if self.args.separate:
+            # we send stdin in several messages
             if header:
-                self.to_send += 1
-                self.host.bridge.messageSend(
+                # first we sent the header
+                try:
+                    await self.host.bridge.messageSend(
+                        dest_jid,
+                        {self.args.lang: header},
+                        subject,
+                        self.args.type,
+                        profile_key=self.profile,
+                    )
+                except Exception as e:
+                    self.disp(f"can't send header: {e}", error=True)
+                    error = True
+
+            to_send.extend({self.args.lang: clean_ustr(l.replace("\n", ""))}
+                           for l in stdin_lines)
+        else:
+            # we sent all in a single message
+            if not (self.args.xhtml or self.args.rich):
+                msg = {self.args.lang: header + clean_ustr("".join(stdin_lines))}
+            else:
+                msg = {}
+            to_send.append(msg)
+
+        for msg in to_send:
+            try:
+                await self.host.bridge.messageSend(
                     dest_jid,
-                    {self.args.lang: header},
-                    subject,
-                    self.args.type,
-                    profile_key=self.profile,
-                    callback=lambda: None,
-                    errback=lambda ignore: ignore,
-                )
-
-            self.to_send += len(stdin_lines)
-            for line in stdin_lines:
-                self.host.bridge.messageSend(
-                    dest_jid,
-                    {self.args.lang: line.replace("\n", "")},
+                    msg,
                     subject,
                     self.args.type,
                     extra,
-                    profile_key=self.host.profile,
-                    callback=self.multi_send_cb,
-                    errback=partial(self.multi_send_eb, msg=line),
-                )
+                    profile_key=self.host.profile)
+            except Exception as e:
+                self.disp(f"can't send message {msg!r}: {e}", error=True)
+                error = True
 
-        else:
-            msg = (
-                {self.args.lang: header + clean_ustr("".join(stdin_lines))}
-                if not (self.args.xhtml or self.args.rich)
-                else {}
-            )
-            self.host.bridge.messageSend(
-                dest_jid,
-                msg,
-                subject,
-                self.args.type,
-                extra,
-                profile_key=self.host.profile,
-                callback=self.host.quit,
-                errback=partial(self.errback,
-                                msg=_("Can't send message: {}")))
+        if error:
+            # at least one message sending failed
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
 
-    def encryptionNamespaceGetCb(self, namespace, jid_):
-        self.host.bridge.messageEncryptionStart(
-            jid_, namespace, not self.args.encrypt_noreplace,
-            self.profile,
-            callback=lambda: self.sendStdin(jid_),
-            errback=partial(self.errback,
-                            msg=_("Can't start encryption session: {}"),
-                            exit_code=C.EXIT_BRIDGE_ERRBACK,
-                            ))
+        self.host.quit()
 
-
-    def start(self):
+    async def start(self):
         if self.args.xhtml and self.args.separate:
             self.disp(
                 "argument -s/--separate is not compatible yet with argument -x/--xhtml",
                 error=True,
             )
-            self.host.quit(2)
+            self.host.quit(C.EXIT_BAD_ARG)
 
-        jids = self.host.check_jids([self.args.jid])
+        jids = await self.host.check_jids([self.args.jid])
         jid_ = jids[0]
 
         if self.args.encrypt_noreplace and self.args.encrypt is None:
             self.parser.error("You need to use --encrypt if you use --encrypt-noreplace")
 
         if self.args.encrypt is not None:
-            self.host.bridge.encryptionNamespaceGet(self.args.encrypt,
-                callback=partial(self.encryptionNamespaceGetCb, jid_=jid_),
-                errback=partial(self.errback,
-                                msg=_("Can't get encryption namespace: {}"),
-                                exit_code=C.EXIT_BRIDGE_ERRBACK,
-                                ))
-        else:
-            self.sendStdin(jid_)
+            try:
+                namespace = await self.host.bridge.encryptionNamespaceGet(
+                    self.args.encrypt)
+            except Exception as e:
+                self.disp(f"can't get encryption namespace: {e}", error=True)
+                self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+            try:
+                await self.host.bridge.messageEncryptionStart(
+                    jid_, namespace, not self.args.encrypt_noreplace, self.profile
+                )
+            except Exception as e:
+                self.disp(f"can't start encryption session: {e}", error=True)
+                self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+        await self.sendStdin(jid_)
 
 
 class MAM(base.CommandBase):
 
     def __init__(self, host):
         super(MAM, self).__init__(
-            host, "mam", use_output=C.OUTPUT_MESS, use_verbose=True, help=_("query archives using MAM"))
-        self.need_loop=True
+            host, "mam", use_output=C.OUTPUT_MESS, use_verbose=True,
+            help=_("query archives using MAM"))
 
     def add_parser_options(self):
         self.parser.add_argument(
@@ -235,9 +222,37 @@
             "--index", dest="rsm_index", type=int,
             help=_("index of the page to retrieve"))
 
-    def _sessionInfosGetCb(self, session_info, data, metadata):
+    async def start(self):
+        extra = {}
+        if self.args.mam_start is not None:
+            extra["mam_start"] = float(self.args.mam_start)
+        if self.args.mam_end is not None:
+            extra["mam_end"] = float(self.args.mam_end)
+        if self.args.mam_with is not None:
+            extra["mam_with"] = self.args.mam_with
+        for suff in ('max', 'after', 'before', 'index'):
+            key = 'rsm_' + suff
+            value = getattr(self.args,key)
+            if value is not None:
+                extra[key] = str(value)
+        try:
+            data, metadata, profile = await self.host.bridge.MAMGet(
+                self.args.service, data_format.serialise(extra), self.profile)
+        except Exception as e:
+            self.disp(f"can't retrieve MAM archives: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+        try:
+            session_info = await self.host.bridge.sessionInfosGet(self.profile)
+        except Exception as e:
+            self.disp(f"can't get session infos: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+        # we need to fill own_jid for message output
         self.host.own_jid = jid.JID(session_info["jid"])
-        self.output(data)
+
+        await self.output(data)
+
         # FIXME: metadata are not displayed correctly and don't play nice with output
         #        they should be added to output data somehow
         if self.verbosity:
@@ -250,29 +265,6 @@
 
         self.host.quit()
 
-    def _MAMGetCb(self, result):
-        data, metadata, profile = result
-        self.host.bridge.sessionInfosGet(self.profile,
-            callback=partial(self._sessionInfosGetCb, data=data, metadata=metadata),
-            errback=self.errback)
-
-    def start(self):
-        extra = {}
-        if self.args.mam_start is not None:
-            extra["mam_start"] = float(self.args.mam_start)
-        if self.args.mam_end is not None:
-            extra["mam_end"] = float(self.args.mam_end)
-        if self.args.mam_with is not None:
-            extra["mam_with"] = self.args.mam_with
-        for suff in ('max', 'after', 'before', 'index'):
-            key = 'rsm_' + suff
-            value = getattr(self.args,key)
-            if value is not None:
-                extra[key] = str(value)
-        self.host.bridge.MAMGet(
-            self.args.service, data_format.serialise(extra), self.profile,
-            callback=self._MAMGetCb, errback=self.errback)
-
 
 class Message(base.CommandBase):
     subcommands = (Send, MAM)