I am trying to implement an argument parser with the argparse
module in Python.
My goal is to allow a variable number of positional input arguments to the main parser and then optionally call a sub parser to perform some task. I'm interested in understanding why my implementation doesn't work as I expect it to and how I could implement something that is as close as possible to what I originally intended.
Here is a minimal example to illustrate what I am trying. I used Python 3.11 to create this example, although I originally encountered the issue using Python 3.9.
In [1]: import argparse
In [2]: parser = argparse.ArgumentParser()
...: parser.add_argument('positional', type=str, nargs='+')
...: parser.add_argument('-f', '--foo')
...:
...: subparsers = parser.add_subparsers(dest='subcommand', required=False)
...: subparser1 = subparsers.add_parser('subcommand1')
...: subparser1.add_argument('-b', '--bar', action='store_true', )
...: subparser2 = subparsers.add_parser('subcommand2')
...: subparser2.add_argument('-b', '--baz', action='store_true')
Out[2]: _StoreTrueAction(option_strings=['-b', '--baz'], dest='baz', nargs=0, const=True, default=False, type=None, choices=None, required=False, help=None, metavar=None)
In [3]: parser.parse_args(['-f', 'a', 'b', 'c', 'd', 'subcommand1'])
Out[3]: Namespace(positional=['b', 'c', 'd'], foo='a', subcommand='subcommand1', bar=False)
In [4]: parser.parse_args(['-f', 'a', 'b', 'c', 'd', 'subcommand2'])
Out[4]: Namespace(positional=['b', 'c', 'd'], foo='a', subcommand='subcommand2', baz=False)
In [5]: parser.parse_args(['-f', 'a', 'b', 'c', 'd'])
usage: ipython [-h] [-f FOO] positional [positional ...] {subcommand1,subcommand2} ...
ipython: error: argument subcommand: invalid choice: 'd' (choose from 'subcommand1', 'subcommand2')
I expected (hoped) that, since required=False
in the add_subparsers
command, that I would have to option of not specifying any sub parser at all. Notably, the In [5]
works, if I set nags='3'
in the positional arguments.
Is this the intended behaviour? If so, what would be the intended way to achieve what I am trying to do?