python: mutually exclusive positional arguments
<p>I want my program to accept mutually exclusive positional arguments, <em>and</em> the usage to be displayed as a group of arguments.<br>
Currently I could only achieve one or the other, but not both...  </p>

<p>This is what I currently have:  </p>

<pre><code>def parse_arguments():
    arg_parser = argparse.ArgumentParser(description = 'Project Builder')

    query_parser = arg_parser.add_argument_group('query', "Query current state")
    build_parser = arg_parser.add_argument_group('build', "Build project")

    # query arguments
    query_parser.add_argument('-s', '--servers',
                 action   = 'store_true',
                 required = False,
                 help     = 'Display available servers')

    query_parser.add_argument('-u', '--users',
                 action   = 'store_true',
                 required = False,
                 help     = 'Display current users')



    # build arguments
    build_parser.add_argument('-f', '--force',
                 action   = 'store',
                 required = False,
                 metavar  = 'SERVER_NAME',
                 help     = 'Force build on SERVER_NAME')

    build_parser.add_argument('-c', '--clean',
                 action   = 'store_true',
                 required = False,
                 help     = 'Clean repo before build')

    build_parser.add_argument('-v', '--verbosity',
                 action   = 'store_true',
                 required = False,
                 help     = 'Print stderr to console')

    build_parser.add_argument('-p', '--project',
                 action   = 'store',
                 required = True,
                 metavar  = 'project_A|project_B|project_C',
                 type     = project_name,
                 help     = 'Project to build (required)')

    return vars(arg_parser.parse_args())


args = parse_arguments()
</code></pre>

<p>Which gives the following:  </p>

<pre><code>usage: test.py [-h] [-s] [-u] [-f SERVER_NAME] [-c] [-v] -p
               project_A|project_B|project_C

Project Builder

optional arguments:
  -h, --help            show this help message and exit

query:
  Query current state

  -s, --servers         Display available servers
  -u, --users           Display current users

build:
  Build project

  -f SERVER_NAME, --force SERVER_NAME
                        Force build on SERVER_NAME
  -c, --clean           Clean repo before build
  -v, --verbosity       Print stderr to console
  -p project_A|project_B|project_C, --project project_A|project_B|project_C
                        Project to build (required)
</code></pre>

<p>But what I really want is for <code>query</code> and <code>build</code> to be two mutually exclusive positional arguments.  </p>

<p>I tried using subparsers as follows:</p>

<pre><code>def parse_arguments():
    arg_parser = argparse.ArgumentParser(description = 'Project Builder')
    command_parser = arg_parser.add_subparsers(help = "Command")

    query_parser = command_parser.add_parser('query', help = "Query current state")
    build_parser = command_parser.add_parser('build', help = "Build project")

    # query arguments
    query_parser.add_argument('-s', '--servers',
                 action   = 'store_true',
                 required = False,
                 help     = 'Display available servers')

    query_parser.add_argument('-u', '--users',
                 action   = 'store_true',
                 required = False,
                 help     = 'Display current users')



    # build arguments
    build_parser.add_argument('-f', '--force',
                 action   = 'store',
                 required = False,
                 metavar  = 'SERVER_NAME',
                 help     = 'Force build on SERVER_NAME')

    build_parser.add_argument('-c', '--clean',
                 action   = 'store_true',
                 required = False,
                 help     = 'Clean repo before build')

    build_parser.add_argument('-v', '--verbosity',
                 action   = 'store_true',
                 required = False,
                 help     = 'Print stderr to console')

    build_parser.add_argument('-p', '--project',
                 action   = 'store',
                 required = True,
                 metavar  = 'project_A|project_B|project_C',
                 type     = project_name,
                 help     = 'Project to build (required)')

    return vars(arg_parser.parse_args())
</code></pre>

<p>But this produces the following:</p>

<pre><code>usage: test.py [-h] {query,build} ...

Project Builder

positional arguments:
  {query,build}  Command
    query        Query current state
    build        Build project

optional arguments:
  -h, --help     show this help message and exit
</code></pre>

<p>Where what I want is a combination of the two attempts above, namely:</p>

<pre><code>usage: test.py [-h] {query,build} ...

Project Builder

optional arguments:
  -h, --help            show this help message and exit

query:
  Query current state

  -s, --servers         Display available servers
  -u, --users           Display current users

build:
  Build project

  -f SERVER_NAME, --force SERVER_NAME
                        Force build on SERVER_NAME
  -c, --clean           Clean repo before build
  -v, --verbosity       Print stderr to console
  -p project_A|project_B|project_C, --project project_A|project_B|project_C
                        Project to build (required)
</code></pre>

<p>Where <code>query</code> and <code>build</code> are mutually exclusive.<br>
I know about the <code>ArgumentParser.add_mutually_exclusive_group(required=False)</code> method, but using it didn't help achieving what I wanted, since 1) the arguments must be optional when using it, and 2) the <code>usage</code> format is not like I want.</p>

<p>In the first scenario, you could give <code>-p</code> choices</p>

<pre><code>build_parser.add_argument('-p', '--project',
             action   = 'store',    # default
             required = True,
             choices  = ['project_A','project_B','project_C'],
             # type     = project_name,    # doesn't make sense
             help     = 'Project to build (required)')
</code></pre>

<p>or as a positional</p>

<pre><code>build_parser.add_argument('project',
             choices  = ['project_A','project_B','project_C'],
             help     = 'Project to build')
</code></pre>

<p><code>type</code> must be a function, one that converts as string to something you want, eg  <code>int('1')</code>, <code>float('12.343')</code>.</p>

<p>The use of subparsers is similar.  To the main parser the subparser argument is just a positional with choices.  But the action taken is to deligate the parsing of the rest of the arguments to the subparser.</p>

<p>Two mutually exclusive positionals doesn't make logical sense</p>

<pre><code> `[foo | bar]`
</code></pre>

<p>It can't tell, based just on position, whether you want to assign the string to <code>foo</code> or to <code>bar</code>.  But you are interested in assigning values based on value, or rather restricting values to a set of choices.</p>

<p>Without looking at it carefully your subparsers code looks right, and should parse the input as you want.  Have you tested it?</p>

<p>The help display for subparsers is not as flexible as it could be, but reworking it takes a fair amount of work (there are SO questions about this).  The current setup is to have separate help displays for the main parser and for each of the subparsers.  There's no builtin comprehensive help display.</p>

python argparse