Argparse: Required arguments listed under "optional arguments"?
Solution 1
Parameters starting with -
or --
are usually considered optional. All other parameters are positional parameters and as such required by design (like positional function arguments). It is possible to require optional arguments, but this is a bit against their design. Since they are still part of the non-positional arguments, they will still be listed under the confusing header “optional arguments” even if they are required. The missing square brackets in the usage part however show that they are indeed required.
See also the documentation:
In general, the argparse module assumes that flags like -f and --bar indicate optional arguments, which can always be omitted at the command line.
Note: Required options are generally considered bad form because users expect options to be optional, and thus they should be avoided when possible.
That being said, the headers “positional arguments” and “optional arguments” in the help are generated by two argument groups in which the arguments are automatically separated into. Now, you could “hack into it” and change the name of the optional ones, but a far more elegant solution would be to create another group for “required 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
Solution 2
Since I prefer to list required arguments before optional, I hack around it via:
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.
Solution 3
Building off of @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
Solution 4
One more time, building off of @RalphyZ
This one doesn't break the exposed 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 show the same as above and should survive 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
Solution 5
by default there're 2 argument groups in parser._action_groups: positional arguments and named arguments (titled 'optional arguments'). you can add your named optional arguments to the existing 'optional arguments' group, and required named arguments to a new 'required arguments' group. After that you can re-order groups:
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
Related videos on Youtube

mort
Updated on February 08, 2022Comments
-
mort 9 months
I use the following simple code to parse some arguments; note that one of them is required. Unfortunately, when the user runs the script without providing the argument, the displayed usage/help text does not indicate that there is a non-optional argument, which I find very confusing. How can I get python to indicate that an argument is not optional?
Here is the code:
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 )
When running above code without providing the required argument, I get the following output:
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
-
Jaime Rodríguez-Guerra over 8 yearsIn the usage line, the
-i INPUT
part is not surrounded by square brackets, which subtlety indicates that is indeed, required. Also, you can manually explain that through thehelp
param -
Asclepius about 6 years@JaimeRGP Yes, but that's not sufficient, of course, and it's also less than prominent. The assigned group name
optional arguments
for the required arguments is still misleading.
-
-
machin over 5 yearsBTW, are there any ways (methods) how to get access to
_action_group
without accessing protected member? In my case I need to add some argument to already existent (custom) group. -
Anthony about 5 yearsDoes this actually force argparse to treat any of the arguments as required?
-
Karl Rosaen about 5 yearsI think the 'required' argument still needs to be set when adding an argument.
-
Jeremy almost 5 yearsThis is great. Solves the --help item showing up in a second optional list.
-
Paul Cezanne almost 5 yearsThat's really nice.
-
user2275693 over 4 years@Anthony - no you need the 'required=True' in add_argument for that. The above answer just illustrates argument grouping.
-
Zarar Mahmud over 3 yearsI have been having the same issue. I tried you solution. It does add the arguments to the new group but my code doesn't seem to work after that. Any solutions would be appreciated. Link to my code - pastebin.com/PvC2aujz
-
Devin over 3 years@ZararMahmud: You are passing in empty arguments in line 24 of your code:
parser.parse_args([])
Instead, useparser.parse_args()
with no arguments to capture the contents of sys.argv. Per argparse -
jeremysprofile about 3 yearsCan you explain how RalphyZ's answer breaks the exposed API?
-
Bryan_D about 3 years
_action_groups
is intended for internal use only. Therefore, there is no compatibility guarantee across versions. -
Judge almost 3 years@poke: Nice solution! But this doesn't help in case you need mutual exclusive groups, or am I missing anything?
-
Peter Moore over 2 years@Judge i would recommend reading this pymotw.com/3/argparse/#mutually-exclusive-options
-
lol over 2 yearsNote: this answer breaks the exposed API, check answer by Bryan_D down below.
-
mrgloom over 2 yearsWhat is the difference between
-
and--
? -
poke over 2 years@mrgloom Options are usually defined with
--
and a precise name to explain what they are for. A single-
can then be used to define a short alias for commonly used options. In my example, you could use both--output out.txt
or-o out.txt
for the exact same thing. The-o
is just a short alias for--output
. Some command line tools additionally allow you to chain these short aliases. E.g.tar -xf archive.tar
is short fortar --extract --file=archive.tar
. -
Isi about 2 yearsNow in the first line it looks as if the argument would be optional, so opposite of what OP shows. Any chance that required arguments are shown before optional ones AND have no brackets?
-
nclark 7 monthsYou're using a protected member
_action_groups
which puts you at serious risk of breaking on a minor release upgrade. I'll stick with @Karl Rosaen's answer. -
nclark 7 monthsAs noted in other answers, the use of private member
_action_groups
violates the API contract and risks breaking on the next minor release. -
nclark 7 monthsmake that @poke's answer - all (useful) answers except his use
_action_groups
. -
Christophe Vu-Brugier 5 monthsI used your snippet for a long time and it served me well. Thank you for that. However, I observe it no longer works with Python 3.10 because "optional arguments" was renamed to "options". Running this code triggers a "KeyError: options" exception on Python 3.10. Consequently, I decided it was safer for my project to drop the distinction between "required" and "optional" arguments.