Get selected subcommand with argparse
--
Music by Eric Matyas
https://www.soundimage.org
Track title: Isolated
--
Chapters
00:00 Question
00:35 Accepted answer (Score 231)
01:05 Answer 2 (Score 35)
01:59 Answer 3 (Score 3)
03:16 Thank you
--
Full question
https://stackoverflow.com/questions/4575...
Accepted answer links:
[Python docs on argparse sub-commands]: http://docs.python.org/dev/library/argpa...
Answer 2 links:
[ArgumentParser.add_subparsers]: https://docs.python.org/3/library/argpar...
--
Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...
--
Tags
#python #commandline #argparse
#avk47
ACCEPTED ANSWER
Score 249
The very bottom of the Python docs on argparse sub-commands explains how to do this:
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-g', '--global')
>>> subparsers = parser.add_subparsers(dest="subparser_name") # this line changed
>>> foo_parser = subparsers.add_parser('foo')
>>> foo_parser.add_argument('-c', '--count')
>>> bar_parser = subparsers.add_parser('bar')
>>> args = parser.parse_args(['-g', 'xyz', 'foo', '--count', '42'])
>>> args
Namespace(count='42', global='xyz', subparser_name='foo')
You can also use the set_defaults() method referenced just above the example I found.
ANSWER 2
Score 36
ArgumentParser.add_subparsers has dest formal argument described as:
dest- name of the attribute under which sub-command name will be stored; by defaultNoneand no value is stored
In the example below of a simple task function layout using subparsers, the selected subparser is in parser.parse_args().subparser.
import argparse
def task_a(alpha):
print('task a', alpha)
def task_b(beta, gamma):
print('task b', beta, gamma)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subparser')
parser_a = subparsers.add_parser('task_a')
parser_a.add_argument(
'-a', '--alpha', dest='alpha', help='Alpha description')
parser_b = subparsers.add_parser('task_b')
parser_b.add_argument(
'-b', '--beta', dest='beta', help='Beta description')
parser_b.add_argument(
'-g', '--gamma', dest='gamma', default=42, help='Gamma description')
kwargs = vars(parser.parse_args())
globals()[kwargs.pop('subparser')](**kwargs)
ANSWER 3
Score 5
Just wanted to post this answer as this came in very handy in some of my recent work. This method makes use of decorators (although not used with conventional @ syntax) and comes in especially handy if the recommended set_defaults is already being used with subparsers.
import argparse
from functools import wraps
import sys
def foo(subparser):
subparser.error('err')
def bar(subparser):
subparser.error('err')
def map_subparser_to_func(func, subparser):
@wraps(func)
def wrapper(*args, **kwargs):
return func(subparser, *args, **kwargs)
return wrapper
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
foo_parser = subparsers.add_parser('foo')
foo_parser.set_defaults(func = map_subparser_to_func(foo, foo_parser))
bar_parser = subparsers.add_parser('bar')
bar_parser.set_defaults(func = map_subparser_to_func(bar, bar_parser))
args = parser.parse_args(sys.argv[1:])
args.func()
The map_subparser_to_func function can be modified to set the subparser to some class attribute or global variable inside of the wrapper function instead of passing it directly and can also be reworked to a conventional decorator for the functions, although that would require adding another layer.
This way there is a direct reference to the object.
ANSWER 4
Score 1
I have nested subparsers and wanted to get the selected
subparser object so I can subparser.error(..) on special cases
where parse_known_args() has invalid unknown args.
I came up with this recursion to solve it. It might only cover
my specific case of subparsers nested with subparsers with
each of them using an explicit dest. But it has the advantage
that it doesn't require using set_defaults and that it gets
the actual subparser object, not just the name of it.
def get_selected_subparser(parser, args):
for action in parser._actions:
if not isinstance(action, argparse._SubParsersAction): continue
choice = getattr(args, action.dest)
if choice is None:
return None
else:
subparser = action.choices[choice]
return get_selected_subparser(subparser, args) or subparser
else:
return None
Now I can do this to print the usage info of the selected subparser:
parser = argparse.ArgumentParser( ... )
# ... definition of parser goes here
args, remainder = parser.parse_known_args()
if remainder_is_invalid():
subparser = get_selected_subparser(parser, args)
subparser.error("unrecognized arguments: {}".format(" ".join(remainder)))