comparison frontends/src/jp/cmd_shell.py @ 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 a42a2478abd2
children 8d9bd5d77336
comparison
equal deleted inserted replaced
2312:eaff25529c53 2313:6ff5212997c7
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 20
21 import base 21 import base
22 import cmd 22 import cmd
23 import sys
23 from sat.core.i18n import _ 24 from sat.core.i18n import _
24 from sat.core import exceptions 25 from sat.core import exceptions
25 from sat_frontends.jp.constants import Const as C 26 from sat_frontends.jp.constants import Const as C
26 from sat.tools.common.ansi import ANSI as A 27 from sat.tools.common.ansi import ANSI as A
27 import shlex 28 import shlex
29 import subprocess
28 30
29 __commands__ = ["Shell"] 31 __commands__ = ["Shell"]
30 INTRO = _(u"""Welcome to {app_name} shell, the Salut à Toi shell ! 32 INTRO = _(u"""Welcome to {app_name} shell, the Salut à Toi shell !
31 33
32 This enrironment helps you using several {app_name} commands with similar parameters. 34 This enrironment helps you using several {app_name} commands with similar parameters.
96 # we check not optional args to see if there 98 # we check not optional args to see if there
97 # is a corresonding parser 99 # is a corresonding parser
98 # else USE args would not work correctly (only for current parser) 100 # else USE args would not work correctly (only for current parser)
99 cmd_args = [] 101 cmd_args = []
100 for arg in args: 102 for arg in args:
101 if arg.startswith(u'-'): 103 if arg.startswith('-'):
102 break 104 break
103 try: 105 try:
104 parser = self.get_cmd_choices(arg, parser) 106 parser = self.get_cmd_choices(arg, parser)
105 except exceptions.NotFound: 107 except exceptions.NotFound:
106 break 108 break
128 pos_args.append(value) 130 pos_args.append(value)
129 else: 131 else:
130 opt_args.append(action.option_strings[0]) 132 opt_args.append(action.option_strings[0])
131 opt_args.append(value) 133 opt_args.append(value)
132 return cmd_args + opt_args + pos_args 134 return cmd_args + opt_args + pos_args
135
136 def run_cmd(self, args, external=False):
137 """run command and retur exit code
138
139 @param args[list[string]]: arguments of the command
140 must not include program name
141 @param external(bool): True if it's an external command (i.e. not jp)
142 @return (int): exit code (0 success, any other int failure)
143 """
144 # FIXME: we have to use subprocess
145 # and relaunch whole python for now
146 # because if host.quit() is called in D-Bus callback
147 # GLib quit the whole app without possibility to stop it
148 # didn't found a nice way to work around it so far
149 # Situation should be better when we'll move away from python-dbus
150 if self.verbose:
151 self.disp(_(u"COMMAND {external}=> {args}").format(
152 external = _(u'(external) ') if external else u'',
153 args=u' '.join(self.format_args(args))))
154 if not external:
155 args = sys.argv[0:1] + args
156 ret_code= subprocess.call(args)
157 # XXX: below is a way to launch the command without creating a new process
158 # may be used when a solution to the aforementioned issue is there
159 # try:
160 # self.host.run(args)
161 # except SystemExit as e:
162 # ret_code = e.code
163 # except Exception as e:
164 # self.disp(A.color(C.A_FAILURE, u'command failed with an exception: {msg}'.format(msg=e)), error=True)
165 # ret_code = 1
166 # else:
167 # ret_code = 0
168
169 if ret_code != 0:
170 self.disp(A.color(C.A_FAILURE, u'command failed with an error code of {err_no}'.format(err_no=ret_code)), error=True)
171 return ret_code
133 172
134 def default(self, args): 173 def default(self, args):
135 """called when no shell command is recognized 174 """called when no shell command is recognized
136 175
137 will launch the command with args on the line 176 will launch the command with args on the line
185 try: 224 try:
186 self.host.run(['--version']) 225 self.host.run(['--version'])
187 except SystemExit: 226 except SystemExit:
188 pass 227 pass
189 228
229 def do_shell(self, args):
230 """launch an external command (you can use ![command] too)"""
231 args = self.parse_args(args)
232 self.run_cmd(args, external=True)
233
190 def do_do(self, args): 234 def do_do(self, args):
191 """lauch a command""" 235 """lauch a command"""
192 # we don't want host to really exit
193 # we want to stay in the loop
194 self.host._no_exit = True
195 args = self.parse_args(args) 236 args = self.parse_args(args)
196 # args may be modified by use_args 237 # args may be modified by use_args
197 # to remove subparsers from it 238 # to remove subparsers from it
198 use_args = self.get_use_args(args) 239 use_args = self.get_use_args(args)
199 cmd_args = self.path + use_args + args 240 cmd_args = self.path + use_args + args
200 if self.verbose: 241 self.run_cmd(cmd_args)
201 self.disp(u"COMMAND => {args}".format(args=u' '.join(self.format_args(cmd_args))))
202 try:
203 self.host.run(cmd_args)
204 except SystemExit as e:
205 if e.code != 0:
206 self.disp(A.color(C.A_FAILURE, u'command failed with an error code of {err_no}'.format(err_no=e.code)), error=True)
207 except Exception as e:
208 self.disp(A.color(C.A_FAILURE, u'command failed with an exception: {msg}'.format(msg=e)), error=True)
209 finally:
210 self.host._no_exit = False
211 242
212 def do_use(self, args): 243 def do_use(self, args):
213 """fix an argument""" 244 """fix an argument"""
214 args = self.parse_args(args) 245 args = self.parse_args(args)
215 if not args: 246 if not args: