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.
This commit is contained in:
Goffi 2019-09-25 08:56:41 +02:00
commit 76aadbb3fb

View file

@ -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 @@ class InputCommon(base.CommandBase):
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 @@ class InputCommon(base.CommandBase):
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 @@ class InputCommon(base.CommandBase):
).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 @@ class InputCommon(base.CommandBase):
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 @@ class InputCommon(base.CommandBase):
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 @@ class Csv(InputCommon):
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,)