changeset 2313:6ff5212997c7

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.
author Goffi <goffi@goffi.org>
date Fri, 07 Jul 2017 22:33:55 +0200
parents eaff25529c53
children 01f0a954d506
files frontends/src/jp/base.py frontends/src/jp/cmd_shell.py
diffstat 2 files changed, 47 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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"""