aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorR. David Murray <rdmurray@bitdance.com>2017-09-06 20:25:40 -0400
committerGitHub <noreply@github.com>2017-09-06 20:25:40 -0400
commit0f6b9d230674da784ca79a0cf1a03d2af5a8b6a8 (patch)
treebe36691f027da9af59e6ae400b7608a5f28794e8 /Lib/argparse.py
parentbpo-21649: Add RFC 7525 and Mozilla server side TLS (#3387) (diff)
downloadcpython-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.py95
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]