# HG changeset patch # User Goffi # Date 1499543114 -7200 # Node ID 8d9bd5d77336bbb13aa13d6b1e93d78e82a0458c # Parent 01f0a954d506576555d771cbab6859cca2b07dfa jp (arg_tools): moved get_cmd_choices, get_use_args and escape to a new arg_tools module, so they can be used in other commands than shell diff -r 01f0a954d506 -r 8d9bd5d77336 frontends/src/jp/arg_tools.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/frontends/src/jp/arg_tools.py Sat Jul 08 21:45:14 2017 +0200 @@ -0,0 +1,93 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +# jp: a SàT command line tool +# Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from sat.core.i18n import _ +from sat.core import exceptions + + +def escape(arg, smart=True): + """format arg with quotes + + @param smart(bool): if True, only escape if needed + """ + if smart and not ' ' in arg and not '"' in arg: + return arg + return u'"' + arg.replace(u'"',u'\\"') + u'"' + + +def get_cmd_choices(cmd=None, parser=None): + try: + choices = parser._subparsers._group_actions[0].choices + return choices[cmd] if cmd is not None else choices + except (KeyError, AttributeError): + raise exceptions.NotFound + + +def get_use_args(host, args, use, verbose=False, parser=None): + """format args for argparse parser with values prefilled + + @param host(JP): jp instance + @param args(list(str)): arguments to use + @param use(dict[str, str]): arguments to fill if found in parser + @param verbose(bool): if True a message will be displayed when argument is used or not + @param parser(argparse.ArgumentParser): parser to use + """ + if parser is None: + parser = host.parser + + # we check not optional args to see if there + # is a corresonding parser + # else USE args would not work correctly (only for current parser) + cmd_args = [] + for arg in args: + if arg.startswith('-'): + break + try: + parser = get_cmd_choices(arg, parser) + except exceptions.NotFound: + break + cmd_args.append(arg) + + # we remove command args + # they'll be in returned list (use_args) + del args[:len(cmd_args)] + + opt_args = [] + pos_args = [] + actions = {a.dest: a for a in parser._actions} + for arg, value in use.iteritems(): + try: + if arg == u'item' and not u'item' in actions: + # small hack when --item is appended to a --items list + arg = u'items' + action = actions[arg] + except KeyError: + if verbose: + host.disp(_(u'ignoring {name}={value}, not corresponding to any argument (in USE)').format( + name=arg, + value=escape(value))) + else: + if verbose: + host.disp(_(u'arg {name}={value} set (in USE)').format(name=arg, value=escape(value))) + if not action.option_strings: + pos_args.append(value) + else: + opt_args.append(action.option_strings[0]) + opt_args.append(value) + return cmd_args + opt_args + pos_args diff -r 01f0a954d506 -r 8d9bd5d77336 frontends/src/jp/cmd_shell.py --- a/frontends/src/jp/cmd_shell.py Sat Jul 08 21:43:36 2017 +0200 +++ b/frontends/src/jp/cmd_shell.py Sat Jul 08 21:45:14 2017 +0200 @@ -24,6 +24,7 @@ from sat.core.i18n import _ from sat.core import exceptions from sat_frontends.jp.constants import Const as C +from sat_frontends.jp import arg_tools from sat.tools.common.ansi import ANSI as A import shlex import subprocess @@ -48,21 +49,12 @@ """parse line arguments""" return shlex.split(args, posix=True) - def get_cmd_choices(self, cmd=None, parser=None): - if parser is None: - parser = self._cur_parser - try: - choices = parser._subparsers._group_actions[0].choices - return choices[cmd] if cmd is not None else choices - except (KeyError, AttributeError): - raise exceptions.NotFound - def update_path(self): self._cur_parser = self.host.parser self.help = u'' for idx, path_elt in enumerate(self.path): try: - self._cur_parser = self.get_cmd_choices(path_elt) + self._cur_parser = arg_tools.get_cmd_choices(path_elt, self._cur_parser) except exceptions.NotFound: self.disp(_(u'bad command path'), error=True) self.path=self.path[:idx] @@ -72,67 +64,21 @@ self.prompt = A.color(C.A_PROMPT_PATH, u'/'.join(self.path)) + A.color(C.A_PROMPT_SUF, u'> ') try: - self.actions = self.get_cmd_choices().keys() + self.actions = arg_tools.get_cmd_choices(parser=self._cur_parser).keys() except exceptions.NotFound: self.actions = [] def add_parser_options(self): pass - def escape_arg(self, arg): - """format arg with quotes""" - return u'"' + arg.replace(u'"',u'\\"') + u'"' - def format_args(self, args): """format argument to be printed with quotes if needed""" for arg in args: if " " in arg: - yield self.escape_arg(arg) + yield arg_tools.escape(arg) else: yield arg - def get_use_args(self, args): - """format args for current parser according to self.use""" - parser = self._cur_parser - - # we check not optional args to see if there - # is a corresonding parser - # else USE args would not work correctly (only for current parser) - cmd_args = [] - for arg in args: - if arg.startswith('-'): - break - try: - parser = self.get_cmd_choices(arg, parser) - except exceptions.NotFound: - break - cmd_args.append(arg) - - # we remove command args - # they'll be in returned list (use_args) - del args[:len(cmd_args)] - - opt_args = [] - pos_args = [] - actions = {a.dest: a for a in parser._actions} - for arg, value in self.use.iteritems(): - try: - action = actions[arg] - except KeyError: - if self.verbose: - self.disp(_(u'ignoring {name}={value}, not corresponding to any argument (in USE)').format( - name=arg, - value=self.escape_arg(value))) - else: - if self.verbose: - self.disp(_(u'arg {name}={value} set (in USE)').format(name=arg, value=self.escape_arg(value))) - if not action.option_strings: - pos_args.append(value) - else: - opt_args.append(action.option_strings[0]) - opt_args.append(value) - return cmd_args + opt_args + pos_args - def run_cmd(self, args, external=False): """run command and retur exit code @@ -236,7 +182,12 @@ args = self.parse_args(args) # args may be modified by use_args # to remove subparsers from it - use_args = self.get_use_args(args) + use_args = arg_tools.get_use_args(self.host, + args, + self.use, + verbose=self.verbose, + parser=self._cur_parser + ) cmd_args = self.path + use_args + args self.run_cmd(cmd_args) @@ -249,14 +200,14 @@ else: self.disp(_(u'arguments in USE:')) for arg, value in self.use.iteritems(): - self.disp(_(A.color(C.A_SUBHEADER, arg, A.RESET, u' = ', self.escape_arg(value)))) + self.disp(_(A.color(C.A_SUBHEADER, arg, A.RESET, u' = ', arg_tools.escape(value)))) elif len(args) != 2: self.disp(u'bad syntax, please use:\nuse [arg] [value]', error=True) else: self.use[args[0]] = u' '.join(args[1:]) if self.verbose: self.disp('set {name} = {value}'.format( - name = args[0], value=self.escape_arg(args[1]))) + name = args[0], value=arg_tools.escape(args[1]))) def do_use_clear(self, args): """unset one or many argument(s) in USE, or all of them if no arg is specified"""