diff sat_frontends/jp/cmd_input.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_input.py	Wed Sep 25 08:53:38 2019 +0200
+++ b/sat_frontends/jp/cmd_input.py	Wed Sep 25 08:56:41 2019 +0200
@@ -18,14 +18,16 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
+import subprocess
+import argparse
+import sys
+import shlex
+import asyncio
 from . import base
 from sat.core.i18n import _
 from sat.core import exceptions
 from sat_frontends.jp.constants import Const as C
 from sat.tools.common.ansi import ANSI as A
-import subprocess
-import argparse
-import sys
 
 __commands__ = ["Input"]
 OPT_STDIN = "stdin"
@@ -105,10 +107,10 @@
             help=_("don't actually run commands but echo what would be launched"),
         )
         self.parser.add_argument(
-            "--log", type=argparse.FileType("wb"), help=_("log stdout to FILE")
+            "--log", type=argparse.FileType("w"), help=_("log stdout to FILE")
         )
         self.parser.add_argument(
-            "--log-err", type=argparse.FileType("wb"), help=_("log stderr to FILE")
+            "--log-err", type=argparse.FileType("w"), help=_("log stderr to FILE")
         )
         self.parser.add_argument("command", nargs=argparse.REMAINDER)
 
@@ -166,15 +168,15 @@
 
         for v in value:
             if arg_type == OPT_STDIN:
-                self._stdin.append(v.encode("utf-8"))
+                self._stdin.append(v)
             elif arg_type == OPT_SHORT:
                 self._opts.append("-{}".format(arg_name))
-                self._opts.append(v.encode("utf-8"))
+                self._opts.append(v)
             elif arg_type == OPT_LONG:
                 self._opts.append("--{}".format(arg_name))
-                self._opts.append(v.encode("utf-8"))
+                self._opts.append(v)
             elif arg_type == OPT_POS:
-                self._pos.append(v.encode("utf-8"))
+                self._pos.append(v)
             elif arg_type == OPT_IGNORE:
                 pass
             else:
@@ -184,7 +186,7 @@
                     ).format(type_=arg_type, name=arg_name)
                 )
 
-    def runCommand(self):
+    async def runCommand(self):
         """run requested command with parsed arguments"""
         if self.args_idx != len(self.args.arguments):
             self.disp(
@@ -200,7 +202,10 @@
         if self.args.debug:
             self.disp(
                 A.color(
-                    C.A_SUBHEADER, _("values: "), A.RESET, ", ".join(self._values_ori)
+                    C.A_SUBHEADER,
+                    _("values: "),
+                    A.RESET,
+                    ", ".join([shlex.quote(a) for a in self._values_ori])
                 ),
                 2,
             )
@@ -209,34 +214,39 @@
                 self.disp(A.color(C.A_SUBHEADER, "--- STDIN ---"))
                 self.disp(stdin)
                 self.disp(A.color(C.A_SUBHEADER, "-------------"))
+
             self.disp(
                 "{indent}{prog} {static} {options} {positionals}".format(
                     indent=4 * " ",
                     prog=sys.argv[0],
                     static=" ".join(self.args.command),
-                    options=" ".join([o for o in self._opts]),
-                    positionals=" ".join([p for p in self._pos]),
+                    options=" ".join(shlex.quote(o) for o in self._opts),
+                    positionals=" ".join(shlex.quote(p) for p in self._pos),
                 )
             )
             self.disp("\n")
         else:
             self.disp(" (" + ", ".join(self._values_ori) + ")", 2, no_lf=True)
             args = [sys.argv[0]] + self.args.command + self._opts + self._pos
-            p = subprocess.Popen(
-                args,
+            p = await asyncio.create_subprocess_exec(
+                *args,
                 stdin=subprocess.PIPE,
                 stdout=subprocess.PIPE,
                 stderr=subprocess.PIPE,
             )
-            (stdout, stderr) = p.communicate(stdin)
+            stdout, stderr = await p.communicate(stdin.encode('utf-8'))
             log = self.args.log
             log_err = self.args.log_err
             log_tpl = "{command}\n{buff}\n\n"
             if log:
-                log.write(log_tpl.format(command=" ".join(args), buff=stdout))
+                log.write(log_tpl.format(
+                    command=" ".join(shlex.quote(a) for a in args),
+                    buff=stdout.decode('utf-8', 'replace')))
             if log_err:
-                log_err.write(log_tpl.format(command=" ".join(args), buff=stderr))
-            ret = p.wait()
+                log_err.write(log_tpl.format(
+                    command=" ".join(shlex.quote(a) for a in args),
+                    buff=stderr.decode('utf-8', 'replace')))
+            ret = p.returncode
             if ret == 0:
                 self.disp(A.color(C.A_SUCCESS, _("OK")))
             else:
@@ -307,21 +317,25 @@
 
         super(Csv, self).filter(filter_type, filter_arg, value)
 
-    def start(self):
+    async def start(self):
         import csv
 
+        if self.args.encoding:
+            sys.stdin.reconfigure(encoding=self.args.encoding, errors="replace")
         reader = csv.reader(sys.stdin)
         for idx, row in enumerate(reader):
             try:
                 if idx < self.args.row:
                     continue
                 for value in row:
-                    self.addValue(value.decode(self.args.encoding))
-                self.runCommand()
+                    self.addValue(value)
+                await self.runCommand()
             except exceptions.CancelError:
                 #  this row has been cancelled, we skip it
                 continue
 
+        self.host.quit()
+
 
 class Input(base.CommandBase):
     subcommands = (Csv,)