In python, how to get subparsers to read in parent parser's argument?

PSA to recent readers

As this question still has visits in 2018, before doing anything this complex with argparse, please consider using docopt or click instead. It will improve both your sanity and that of anyone who might read or modify your code. Thank you.

Original answer

As is, you have a few issues.

First, parser.parse_args is a method that returns a namespace of parser's arguments, so you should do something like

args = parser.parse_args()

Then args.main_args to get-main_arg from a call like

program -main_arg run required_sub_arg -arg_a

Your issue with main_arg is that you have created a argument to parser named main_arg, and you make a call like

program run required_sub_arg -main_arg -arg_a

that refers to an argument to a named main_arg. Since a doesn't have such an argument, it is invalid.

In order to refer to a parser's argument from one of its subparser, you have to make said subparser inherit the arguments of its parent. This is done with

a=parser.add_subparser('run', parents=[parser])

You have mistaken subparser for child parser. See http://docs.python.org/dev/py3k/library/argparse.html and https://code.google.com/p/argparse/issues/detail?id=54 for more informations.


For anyone else using argparse that arrives here looking for a way to display "common" sub-parser arguments in the "main" help screen, here's one approach:

import argparse
common = argparse.ArgumentParser(add_help=False)
common.add_argument('--shared', action='store_true', help='some shared arg')
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--parent', action='store_true', help='parent only arg')
subparsers = parser.add_subparsers()
run = subparsers.add_parser('run', parents=[common])
run.add_argument('--fast', action='store_true', help='run only arg')
parser.epilog = "--- Arguments common to all sub-parsers ---" \
    + common.format_help().replace(common.format_usage(), '')
args = parser.parse_args()

Main help:

$ program.py -h
usage: program.py [-h] {run} ...

positional arguments:
  {run}

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

--- Arguments common to all sub-parsers ---
optional arguments:
  --shared  some shared arg

run sub-parser help:

$ program.py run -h
usage: program.py run [-h] [--shared]

optional arguments:
  -h, --help  show this help message and exit
  --shared    some shared arg
  --fast      run only arg

To address the actual question, since the accepted answer doesn't run for me, here's some additional information on why it doesn't seem possible to truly share argparse arguments with the same name across both parent and child/sub-parser parsers.

First, the problem with the following code:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-main_arg')
subparsers = parser.add_subparsers()
run = subparsers.add_parser('run', parents=[parser])
args = parser.parse_args()

Is that it leads to the following error, because both parent parser and sub-parser run define the -h/--help argument (by default).

Argparse.ArgumentError: argument -h/--help: conflicting option strings: -h, --help

While this error can be avoided by suppressing the -h/--help option (with add_help=False) on either the parent or the child, it's nice to have the help option at both levels.

Another potential way to avoid conflicting help options is to move common arguments to a shared parser, common:

import argparse
common = argparse.ArgumentParser(add_help=False)
common.add_argument('-main_arg', action='store_true')
parser = argparse.ArgumentParser(parents=[common])
subparsers = parser.add_subparsers()
run = subparsers.add_parser('run', parents=[common])
args = parser.parse_args()
print(args)

While this appears to work on the surface, in practice, it doesn't work as intended:

$ program.py run  # OK
Namespace(main_arg=False)
$ program.py run -main_arg  # OK
Namespace(main_arg=True)
$ program.py -main_arg run  # BAD: expected main_arg to be True
Namespace(main_arg=False)

The behavior observed when parsing program.py -main_arg run illustrates a key relationship: a parent argparser and its sub-parsers are independent parsers, where the parent parses all arguments up to the sub-parser "command" positional argument, and then the selected sub-parser parses the remaining arguments in the same Namespace as the parent with no regard for attributes that may have been set by the parent.