Mercurial > libervia-backend
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: |