mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
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.
This commit is contained in:
parent
ad0ffa033e
commit
0f6b9d2306
5 changed files with 235 additions and 3 deletions
|
@ -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)
|
||||
|
@ -2224,6 +2230,91 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
|||
# return the pattern
|
||||
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
|
||||
# ========================
|
||||
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue