Mercurial > libervia-backend
comparison sat_frontends/jp/cmd_shell.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | frontends/src/jp/cmd_shell.py@0046283a285d |
children | 56f94936df1e |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # jp: a SàT command line tool | |
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 import base | |
22 import cmd | |
23 import sys | |
24 from sat.core.i18n import _ | |
25 from sat.core import exceptions | |
26 from sat_frontends.jp.constants import Const as C | |
27 from sat_frontends.jp import arg_tools | |
28 from sat.tools.common.ansi import ANSI as A | |
29 import shlex | |
30 import subprocess | |
31 | |
32 __commands__ = ["Shell"] | |
33 INTRO = _(u"""Welcome to {app_name} shell, the Salut à Toi shell ! | |
34 | |
35 This enrironment helps you using several {app_name} commands with similar parameters. | |
36 | |
37 To quit, just enter "quit" or press C-d. | |
38 Enter "help" or "?" to know what to do | |
39 """).format(app_name = C.APP_NAME) | |
40 | |
41 | |
42 class Shell(base.CommandBase, cmd.Cmd): | |
43 | |
44 def __init__(self, host): | |
45 base.CommandBase.__init__(self, host, 'shell', help=_(u'launch jp in shell (REPL) mode')) | |
46 cmd.Cmd.__init__(self) | |
47 | |
48 def parse_args(self, args): | |
49 """parse line arguments""" | |
50 return shlex.split(args, posix=True) | |
51 | |
52 def update_path(self): | |
53 self._cur_parser = self.host.parser | |
54 self.help = u'' | |
55 for idx, path_elt in enumerate(self.path): | |
56 try: | |
57 self._cur_parser = arg_tools.get_cmd_choices(path_elt, self._cur_parser) | |
58 except exceptions.NotFound: | |
59 self.disp(_(u'bad command path'), error=True) | |
60 self.path=self.path[:idx] | |
61 break | |
62 else: | |
63 self.help = self._cur_parser | |
64 | |
65 self.prompt = A.color(C.A_PROMPT_PATH, u'/'.join(self.path)) + A.color(C.A_PROMPT_SUF, u'> ') | |
66 try: | |
67 self.actions = arg_tools.get_cmd_choices(parser=self._cur_parser).keys() | |
68 except exceptions.NotFound: | |
69 self.actions = [] | |
70 | |
71 def add_parser_options(self): | |
72 pass | |
73 | |
74 def format_args(self, args): | |
75 """format argument to be printed with quotes if needed""" | |
76 for arg in args: | |
77 if " " in arg: | |
78 yield arg_tools.escape(arg) | |
79 else: | |
80 yield arg | |
81 | |
82 def run_cmd(self, args, external=False): | |
83 """run command and retur exit code | |
84 | |
85 @param args[list[string]]: arguments of the command | |
86 must not include program name | |
87 @param external(bool): True if it's an external command (i.e. not jp) | |
88 @return (int): exit code (0 success, any other int failure) | |
89 """ | |
90 # FIXME: we have to use subprocess | |
91 # and relaunch whole python for now | |
92 # because if host.quit() is called in D-Bus callback | |
93 # GLib quit the whole app without possibility to stop it | |
94 # didn't found a nice way to work around it so far | |
95 # Situation should be better when we'll move away from python-dbus | |
96 if self.verbose: | |
97 self.disp(_(u"COMMAND {external}=> {args}").format( | |
98 external = _(u'(external) ') if external else u'', | |
99 args=u' '.join(self.format_args(args)))) | |
100 if not external: | |
101 args = sys.argv[0:1] + args | |
102 ret_code= subprocess.call(args) | |
103 # XXX: below is a way to launch the command without creating a new process | |
104 # may be used when a solution to the aforementioned issue is there | |
105 # try: | |
106 # self.host.run(args) | |
107 # except SystemExit as e: | |
108 # ret_code = e.code | |
109 # except Exception as e: | |
110 # self.disp(A.color(C.A_FAILURE, u'command failed with an exception: {msg}'.format(msg=e)), error=True) | |
111 # ret_code = 1 | |
112 # else: | |
113 # ret_code = 0 | |
114 | |
115 if ret_code != 0: | |
116 self.disp(A.color(C.A_FAILURE, u'command failed with an error code of {err_no}'.format(err_no=ret_code)), error=True) | |
117 return ret_code | |
118 | |
119 def default(self, args): | |
120 """called when no shell command is recognized | |
121 | |
122 will launch the command with args on the line | |
123 (i.e. will launch do [args]) | |
124 """ | |
125 if args=='EOF': | |
126 self.do_quit('') | |
127 self.do_do(args) | |
128 | |
129 def do_help(self, args): | |
130 """show help message""" | |
131 if not args: | |
132 self.disp(A.color(C.A_HEADER, _(u'Shell commands:')), no_lf=True) | |
133 super(Shell, self).do_help(args) | |
134 if not args: | |
135 self.disp(A.color(C.A_HEADER, _(u'Action commands:'))) | |
136 help_list = self._cur_parser.format_help().split('\n\n') | |
137 print('\n\n'.join(help_list[1 if self.path else 2:])) | |
138 | |
139 def do_debug(self, args): | |
140 """launch internal debugger""" | |
141 try: | |
142 import ipdb as pdb | |
143 except ImportError: | |
144 import pdb | |
145 pdb.set_trace() | |
146 | |
147 def do_verbose(self, args): | |
148 """show verbose mode, or (de)activate it""" | |
149 args = self.parse_args(args) | |
150 if args: | |
151 self.verbose = C.bool(args[0]) | |
152 self.disp(_(u'verbose mode is {status}').format( | |
153 status = _(u'ENABLED') if self.verbose else _(u'DISABLED'))) | |
154 | |
155 def do_cmd(self, args): | |
156 """change command path""" | |
157 if args == '..': | |
158 self.path = self.path[:-1] | |
159 else: | |
160 if not args or args[0] == '/': | |
161 self.path = [] | |
162 args = '/'.join(args.split()) | |
163 for path_elt in args.split('/'): | |
164 path_elt = path_elt.strip() | |
165 if not path_elt: | |
166 continue | |
167 self.path.append(path_elt) | |
168 self.update_path() | |
169 | |
170 def do_version(self, args): | |
171 """show current SàT/jp version""" | |
172 try: | |
173 self.host.run(['--version']) | |
174 except SystemExit: | |
175 pass | |
176 | |
177 def do_shell(self, args): | |
178 """launch an external command (you can use ![command] too)""" | |
179 args = self.parse_args(args) | |
180 self.run_cmd(args, external=True) | |
181 | |
182 def do_do(self, args): | |
183 """lauch a command""" | |
184 args = self.parse_args(args) | |
185 if (self._not_default_profile and | |
186 not '-p' in args and | |
187 not '--profile' in args and | |
188 not 'profile' in self.use): | |
189 # profile is not specified and we are not using the default profile | |
190 # so we need to add it in arguments to use current user profile | |
191 if self.verbose: | |
192 self.disp(_(u'arg profile={profile} (logged profile)').format(profile=self.profile)) | |
193 use = self.use.copy() | |
194 use['profile'] = self.profile | |
195 else: | |
196 use = self.use | |
197 | |
198 | |
199 # args may be modified by use_args | |
200 # to remove subparsers from it | |
201 parser_args, use_args = arg_tools.get_use_args(self.host, | |
202 args, | |
203 use, | |
204 verbose=self.verbose, | |
205 parser=self._cur_parser | |
206 ) | |
207 cmd_args = self.path + parser_args + use_args | |
208 self.run_cmd(cmd_args) | |
209 | |
210 def do_use(self, args): | |
211 """fix an argument""" | |
212 args = self.parse_args(args) | |
213 if not args: | |
214 if not self.use: | |
215 self.disp(_(u'no argument in USE')) | |
216 else: | |
217 self.disp(_(u'arguments in USE:')) | |
218 for arg, value in self.use.iteritems(): | |
219 self.disp(_(A.color(C.A_SUBHEADER, arg, A.RESET, u' = ', arg_tools.escape(value)))) | |
220 elif len(args) != 2: | |
221 self.disp(u'bad syntax, please use:\nuse [arg] [value]', error=True) | |
222 else: | |
223 self.use[args[0]] = u' '.join(args[1:]) | |
224 if self.verbose: | |
225 self.disp('set {name} = {value}'.format( | |
226 name = args[0], value=arg_tools.escape(args[1]))) | |
227 | |
228 def do_use_clear(self, args): | |
229 """unset one or many argument(s) in USE, or all of them if no arg is specified""" | |
230 args = self.parse_args(args) | |
231 if not args: | |
232 self.use.clear() | |
233 else: | |
234 for arg in args: | |
235 try: | |
236 del self.use[arg] | |
237 except KeyError: | |
238 self.disp(A.color(C.A_FAILURE, _(u'argument {name} not found').format(name=arg)), error=True) | |
239 else: | |
240 if self.verbose: | |
241 self.disp(_(u'argument {name} removed').format(name=arg)) | |
242 | |
243 def do_whoami(self, args): | |
244 u"""print profile currently used""" | |
245 self.disp(self.profile) | |
246 | |
247 def do_quit(self, args): | |
248 u"""quit the shell""" | |
249 self.disp(_(u'good bye!')) | |
250 self.host.quit() | |
251 | |
252 def do_exit(self, args): | |
253 u"""alias for quit""" | |
254 self.do_quit(args) | |
255 | |
256 def start(self): | |
257 default_profile = self.host.bridge.profileNameGet(C.PROF_KEY_DEFAULT) | |
258 self._not_default_profile = self.profile != default_profile | |
259 self.path = [] | |
260 self._cur_parser = self.host.parser | |
261 self.use = {} | |
262 self.verbose = False | |
263 self.update_path() | |
264 self.cmdloop(INTRO.encode('utf-8')) |