Coder Perfect

Required arguments are listed under “optional arguments” in Argparse?

Problem

To parse several arguments, I use the following simple code; note that one of them is necessary. Unfortunately, the displayed usage/help text does not show that there is a non-optional argument when the user executes the script without specifying the argument, which I find quite puzzling. How can I tell Python that a certain argument is not optional?

The code is as follows:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Foo')
    parser.add_argument('-i','--input', help='Input file name', required=True)
    parser.add_argument('-o','--output', help='Output file name', default="stdout")
    args = parser.parse_args()
    print ("Input file: %s" % args.input )
    print ("Output file: %s" % args.output )

I receive the following output when I run the above code without adding the needed argument:

usage: foo.py [-h] -i INPUT [-o OUTPUT]

Foo

optional arguments:
    -h, --help            show this help message and exit
    -i INPUT, --input INPUT
                          Input file name
    -o OUTPUT, --output OUTPUT
                          Output file name

Asked by mort

Solution #1

Optional parameters are those that begin with the letters – or —. The remaining parameters are all positional parameters, and as such, are necessary by design (like positional function arguments). It is possible to make optional arguments mandatory, but this goes against the nature of their design. Even though they are needed, they will be listed under the confusing category “optional arguments” because they are still part of the non-positional arguments. However, the absence of square brackets in the usage section indicates that they are essential.

Also see the following documentation:

The headers “positional arguments” and “optional arguments” in the assistance, on the other hand, are generated by two argument groups into which the arguments are automatically split. You could “hack into it” and rename the optional ones, but a lot more elegant way would be to establish a new group for “mandatory named arguments” (or whatever you want to call them):

parser = argparse.ArgumentParser(description='Foo')
parser.add_argument('-o', '--output', help='Output file name', default='stdout')
requiredNamed = parser.add_argument_group('required named arguments')
requiredNamed.add_argument('-i', '--input', help='Input file name', required=True)
parser.parse_args(['-h'])
usage: [-h] [-o OUTPUT] -i INPUT

Foo

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name

required named arguments:
  -i INPUT, --input INPUT
                        Input file name

Answered by poke

Solution #2

Because I like to show mandatory parameters before optional options, I use the following hack:

parser = argparse.ArgumentParser()
parser._action_groups.pop()
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
return parser.parse_args()

and this outputs:

usage: main.py [-h] --required_arg REQUIRED_ARG [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

optional arguments:
  --optional_arg OPTIONAL_ARG

I can live without -h, –help showing up in the optional arguments group.

Answered by Karl Rosaen

Solution #3

Taking inspiration from @Karl Rosaen

parser = argparse.ArgumentParser()
optional = parser._action_groups.pop() # Edited this line
required = parser.add_argument_group('required arguments')
# remove this line: optional = parser...
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')
parser._action_groups.append(optional) # added this line
return parser.parse_args()

and this outputs:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

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

Answered by RalphyZ

Solution #4

Building on @RalphyZ’s work, I’ll say it again.

This one doesn’t cause any issues with the public API.

from argparse import ArgumentParser, SUPPRESS
# Disable default help
parser = ArgumentParser(add_help=False)
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')

# Add back help 
optional.add_argument(
    '-h',
    '--help',
    action='help',
    default=SUPPRESS,
    help='show this help message and exit'
)
required.add_argument('--required_arg', required=True)
optional.add_argument('--optional_arg')

Which will display the same information as before and should be preserved in future versions:

usage: main.py [-h] [--required_arg REQUIRED_ARG]
           [--optional_arg OPTIONAL_ARG]

required arguments:
  --required_arg REQUIRED_ARG

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

Answered by Bryan_D

Solution #5

Positional arguments and named arguments (labeled ‘optional arguments’) are the two types of arguments in parser. action groups by default. Named optional arguments can be added to the current ‘optional arguments’ group, while necessary named arguments can be added to a new’required arguments’ group. After that, you can rearrange the groupings as follows:

import argparse

parser = argparse.ArgumentParser(description='Foo')

required = parser.add_argument_group('required arguments')

required.add_argument('-i','--input', help='Input file name', required=True)
parser.add_argument('-o','--output', help='Output file name', default="stdout")

groups_order = {
    'positional arguments': 0,
    'required arguments': 1,
    'optional arguments': 2
}
parser._action_groups.sort(key=lambda g: groups_order[g.title])

parser.parse_args(['-h'])

output:

usage: argparse_argument_groups.py [-h] -i INPUT [-o OUTPUT]

Foo

required arguments:
  -i INPUT, --input INPUT
                        Input file name

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name

Answered by atti

Post is based on https://stackoverflow.com/questions/24180527/argparse-required-arguments-listed-under-optional-arguments