# HG changeset patch # User Goffi # Date 1499459635 -7200 # Node ID 6ff5212997c7f0c870c1e2a7338e5edaa485fbd3 # Parent eaff25529c530858ed3701ab4d8ae672311aaab6 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. diff -r eaff25529c53 -r 6ff5212997c7 frontends/src/jp/base.py --- a/frontends/src/jp/base.py Fri Jul 07 12:45:28 2017 +0200 +++ b/frontends/src/jp/base.py Fri Jul 07 22:33:55 2017 +0200 @@ -165,9 +165,6 @@ 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 @@ 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 diff -r eaff25529c53 -r 6ff5212997c7 frontends/src/jp/cmd_shell.py --- a/frontends/src/jp/cmd_shell.py Fri Jul 07 12:45:28 2017 +0200 +++ b/frontends/src/jp/cmd_shell.py Fri Jul 07 22:33:55 2017 +0200 @@ -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 @@ # 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 @@ 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 @@ 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"""