jp (shell): use of subprocess instead of running commands in the same process:

when a command is calling host.quit() in a D-Bus callback, GLib mainloop is exiting and quit the whole process without (apparently?) any way to block it. Couldn't find a good way to avoid that, so subprocess are now used as a workaround, instead of launching commands in the same process. When we'll get rid of python-dbus, there may be an other way.

Execuring external commands is now possible through the shell/! command.
This commit is contained in:
Goffi 2017-07-07 22:33:55 +02:00
commit 62c17395bd
2 changed files with 47 additions and 20 deletions

View file

@ -165,9 +165,6 @@ class Jp(object):
self.bridge = bridge_module.Bridge()
self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
# _no_exit is used in shell mode because SystemExit can't
# catched in some cases
self._no_exit = False
def _bridgeCb(self):
self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
@ -445,8 +442,7 @@ class Jp(object):
callback(*args, **kwargs)
self.stop_loop()
if not self._no_exit:
sys.exit(errcode)
sys.exit(errcode)
def check_jids(self, jids):
"""Check jids validity, transform roster name to corresponding jids

View file

@ -20,11 +20,13 @@
import base
import cmd
import sys
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 shlex
import subprocess
__commands__ = ["Shell"]
INTRO = _(u"""Welcome to {app_name} shell, the Salut à Toi shell !
@ -98,7 +100,7 @@ class Shell(base.CommandBase, cmd.Cmd):
# else USE args would not work correctly (only for current parser)
cmd_args = []
for arg in args:
if arg.startswith(u'-'):
if arg.startswith('-'):
break
try:
parser = self.get_cmd_choices(arg, parser)
@ -131,6 +133,43 @@ class Shell(base.CommandBase, cmd.Cmd):
opt_args.append(value)
return cmd_args + opt_args + pos_args
def run_cmd(self, args, external=False):
"""run command and retur exit code
@param args[list[string]]: arguments of the command
must not include program name
@param external(bool): True if it's an external command (i.e. not jp)
@return (int): exit code (0 success, any other int failure)
"""
# FIXME: we have to use subprocess
# and relaunch whole python for now
# because if host.quit() is called in D-Bus callback
# GLib quit the whole app without possibility to stop it
# didn't found a nice way to work around it so far
# Situation should be better when we'll move away from python-dbus
if self.verbose:
self.disp(_(u"COMMAND {external}=> {args}").format(
external = _(u'(external) ') if external else u'',
args=u' '.join(self.format_args(args))))
if not external:
args = sys.argv[0:1] + args
ret_code= subprocess.call(args)
# XXX: below is a way to launch the command without creating a new process
# may be used when a solution to the aforementioned issue is there
# try:
# self.host.run(args)
# except SystemExit as e:
# ret_code = e.code
# except Exception as e:
# self.disp(A.color(C.A_FAILURE, u'command failed with an exception: {msg}'.format(msg=e)), error=True)
# ret_code = 1
# else:
# ret_code = 0
if ret_code != 0:
self.disp(A.color(C.A_FAILURE, u'command failed with an error code of {err_no}'.format(err_no=ret_code)), error=True)
return ret_code
def default(self, args):
"""called when no shell command is recognized
@ -187,27 +226,19 @@ class Shell(base.CommandBase, cmd.Cmd):
except SystemExit:
pass
def do_shell(self, args):
"""launch an external command (you can use ![command] too)"""
args = self.parse_args(args)
self.run_cmd(args, external=True)
def do_do(self, args):
"""lauch a command"""
# we don't want host to really exit
# we want to stay in the loop
self.host._no_exit = True
args = self.parse_args(args)
# args may be modified by use_args
# to remove subparsers from it
use_args = self.get_use_args(args)
cmd_args = self.path + use_args + args
if self.verbose:
self.disp(u"COMMAND => {args}".format(args=u' '.join(self.format_args(cmd_args))))
try:
self.host.run(cmd_args)
except SystemExit as e:
if e.code != 0:
self.disp(A.color(C.A_FAILURE, u'command failed with an error code of {err_no}'.format(err_no=e.code)), error=True)
except Exception as e:
self.disp(A.color(C.A_FAILURE, u'command failed with an exception: {msg}'.format(msg=e)), error=True)
finally:
self.host._no_exit = False
self.run_cmd(cmd_args)
def do_use(self, args):
"""fix an argument"""