diff options
author | R. David Murray <rdmurray@bitdance.com> | 2017-09-06 20:25:40 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-06 20:25:40 -0400 |
commit | 0f6b9d230674da784ca79a0cf1a03d2af5a8b6a8 (patch) | |
tree | be36691f027da9af59e6ae400b7608a5f28794e8 /Lib/argparse.py | |
parent | bpo-21649: Add RFC 7525 and Mozilla server side TLS (#3387) (diff) | |
download | cpython-0f6b9d230674da784ca79a0cf1a03d2af5a8b6a8.tar.gz cpython-0f6b9d230674da784ca79a0cf1a03d2af5a8b6a8.tar.bz2 cpython-0f6b9d230674da784ca79a0cf1a03d2af5a8b6a8.zip |
bpo-14191 Add parse_intermixed_args. (#3319)
This adds support for parsing a command line where options and positionals are intermixed as is common in many unix commands. This is paul.j3's patch with a few tweaks.
Diffstat (limited to 'Lib/argparse.py')
-rw-r--r-- | Lib/argparse.py | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/Lib/argparse.py b/Lib/argparse.py index b69c5adfa07..d59e645203c 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -587,6 +587,8 @@ class HelpFormatter(object): result = '...' elif action.nargs == PARSER: result = '%s ...' % get_metavar(1) + elif action.nargs == SUPPRESS: + result = '' else: formats = ['%s' for _ in range(action.nargs)] result = ' '.join(formats) % get_metavar(action.nargs) @@ -2212,6 +2214,10 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): elif nargs == PARSER: nargs_pattern = '(-*A[-AO]*)' + # suppress action, like nargs=0 + elif nargs == SUPPRESS: + nargs_pattern = '(-*-*)' + # all others should be integers else: nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) @@ -2225,6 +2231,91 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): return nargs_pattern # ======================== + # Alt command line argument parsing, allowing free intermix + # ======================== + + def parse_intermixed_args(self, args=None, namespace=None): + args, argv = self.parse_known_intermixed_args(args, namespace) + if argv: + msg = _('unrecognized arguments: %s') + self.error(msg % ' '.join(argv)) + return args + + def parse_known_intermixed_args(self, args=None, namespace=None): + # returns a namespace and list of extras + # + # positional can be freely intermixed with optionals. optionals are + # first parsed with all positional arguments deactivated. The 'extras' + # are then parsed. If the parser definition is incompatible with the + # intermixed assumptions (e.g. use of REMAINDER, subparsers) a + # TypeError is raised. + # + # positionals are 'deactivated' by setting nargs and default to + # SUPPRESS. This blocks the addition of that positional to the + # namespace + + positionals = self._get_positional_actions() + a = [action for action in positionals + if action.nargs in [PARSER, REMAINDER]] + if a: + raise TypeError('parse_intermixed_args: positional arg' + ' with nargs=%s'%a[0].nargs) + + if [action.dest for group in self._mutually_exclusive_groups + for action in group._group_actions if action in positionals]: + raise TypeError('parse_intermixed_args: positional in' + ' mutuallyExclusiveGroup') + + try: + save_usage = self.usage + try: + if self.usage is None: + # capture the full usage for use in error messages + self.usage = self.format_usage()[7:] + for action in positionals: + # deactivate positionals + action.save_nargs = action.nargs + # action.nargs = 0 + action.nargs = SUPPRESS + action.save_default = action.default + action.default = SUPPRESS + namespace, remaining_args = self.parse_known_args(args, + namespace) + for action in positionals: + # remove the empty positional values from namespace + if (hasattr(namespace, action.dest) + and getattr(namespace, action.dest)==[]): + from warnings import warn + warn('Do not expect %s in %s' % (action.dest, namespace)) + delattr(namespace, action.dest) + finally: + # restore nargs and usage before exiting + for action in positionals: + action.nargs = action.save_nargs + action.default = action.save_default + optionals = self._get_optional_actions() + try: + # parse positionals. optionals aren't normally required, but + # they could be, so make sure they aren't. + for action in optionals: + action.save_required = action.required + action.required = False + for group in self._mutually_exclusive_groups: + group.save_required = group.required + group.required = False + namespace, extras = self.parse_known_args(remaining_args, + namespace) + finally: + # restore parser values before exiting + for action in optionals: + action.required = action.save_required + for group in self._mutually_exclusive_groups: + group.required = group.save_required + finally: + self.usage = save_usage + return namespace, extras + + # ======================== # Value conversion methods # ======================== def _get_values(self, action, arg_strings): @@ -2270,6 +2361,10 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): value = [self._get_value(action, v) for v in arg_strings] self._check_value(action, value[0]) + # SUPPRESS argument does not put anything in the namespace + elif action.nargs == SUPPRESS: + value = SUPPRESS + # all other types of nargs produce a list else: value = [self._get_value(action, v) for v in arg_strings] |