I'd like to have a program that takes a --action=
flag, where the valid choices are dump
and upload
, with upload
being the default. If (and only if) dump
is selected, I'd like there to also be a --dump-format=
option. Is there a way to express this using argparse, or do I need to just accept all the arguments and do the logic myself.

- 14,353
- 9
- 63
- 113
-
Is it a viable option (aesthetically speaking) to do something like `--action=dump-csv` or `--action=dump-some-other-format`? This would alleviate the problem of having "required options" entirely. – dcrosta Feb 29 '12 at 20:32
-
@dcrosta it would obviously work, but I prefer not to go that way, I find it unwieldy. – Alex Gaynor Feb 29 '12 at 20:35
-
Fair enough, just wanted to make sure you've covered the obvious bases. – dcrosta Feb 29 '12 at 20:37
-
He needed default to be upload -- parser.add_argument('--action', choices=['upload', 'dump'], default='dump') but I did not think of parser.error. – Iman Feb 29 '12 at 20:53
5 Answers
The argparse module offers a way to do this without implementing your own requiredness checks. The example below uses "subparsers" or "sub commands". I've implemented a subparser for "dump" and one for "format".
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', help='The file you want to act on.')
subparsers = parser.add_subparsers(dest='subcommand')
subparsers.required = True # required since 3.7
# subparser for dump
parser_dump = subparsers.add_parser('dump')
# add a required argument
parser_dump.add_argument(
'format',
choices=['csv', 'json'],
help='Dump the file in this format.')
# subparser for upload
parser_upload = subparsers.add_parser('upload')
# add a required argument
parser_upload.add_argument(
'server',
choices=['amazon', 'imgur'],
help='Upload the file to this service.')
args = parser.parse_args()
print args
if args.subcommand == 'dump':
print 'I will now dump "%s" in the %s format' % (args.file, args.format)
if args.subcommand == 'upload':
print 'I will now upload "%s" to %s' % (args.file, args.server)
That looks like this on the command line:
$ python ap.py
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments
$ python ap.py tmp.txt
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments
Upload:
$ python ap.py tmp.txt upload
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: too few arguments
$ python ap.py tmp.txt upload amazo
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: argument server: invalid choice: 'amazo' (choose from 'amazon', 'imgur')
$ python ap.py tmp.txt upload amazon
Namespace(file='tmp.txt', server='amazon', subcommand='upload')
I will now upload "tmp.txt" to amazon
$ python ap.py tmp.txt upload imgur
Namespace(file='tmp.txt', server='imgur', subcommand='upload')
I will now upload "tmp.txt" to imgur
Dump:
$ python ap.py tmp.txt dump
usage: ap.py file dump [-h] {csv,json}
ap.py file dump: error: too few arguments
$ python ap.py tmp.txt dump csv
Namespace(file='tmp.txt', format='csv', subcommand='dump')
I will now dump "tmp.txt" in the csv format
$ python ap.py tmp.txt dump json
Namespace(file='tmp.txt', format='json', subcommand='dump')
I will now dump "tmp.txt" in the json format
More info: ArgumentParser.add_subparsers()
-
The design of the command line arguments are different than the one asked. – debashish.ghosh Oct 06 '21 at 20:10
Another way to approach the problem is by using subcommands (a'la git) with "action" as the first argument:
script dump --dump-format="foo"
script upload

- 633
- 1
- 5
- 7
-
-
1This is wrong. You can add as many subparsers as you want. See subcommands link referenced in answer. See also [Niels Bom's answer](https://stackoverflow.com/a/13706448/2311167) – Adrian W Jun 16 '19 at 20:41
-
... or did you mean _nesting_ of subcommands? Something like `dump dumpsubcommand1` , `dump subcommand2`, `upload subcommand3` etc..? – Adrian W Jun 16 '19 at 21:05
You could use parser.error
:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--action', choices=['upload', 'dump'], default='dump')
parser.add_argument('--dump-format')
args = parser.parse_args()
if args.action != 'dump' and args.dump_format:
parser.error('--dump-format can only be set when --action=dump.')

- 15,930
- 5
- 50
- 52
-
12This is only describing the "argparse way" of reporting an error. The logic is implemented manually. Not an "argparse" way of conditionally requiring arguments. – Niels Bom Dec 04 '12 at 15:07
-
1If instead of "dump" and "format" there are 5 more options that all have their own set of required or non-required arguments this way of checking for requiredness can get unclear fast. – Niels Bom Dec 04 '12 at 15:22
-
Besides, things like argparse.FileType will get problems. For certain selected actions when the FileType argument is not needed, non-existence of the file causes a false error. – Joshua Chia Dec 26 '12 at 15:56
It depends what you consider "doing all the logic yourself". You can still use argparse and add the dump option as follows with minimal effort without resorting to sub-commands:
from argparse import ArgumentParser
from sys import argv
parser = ArgumentParser()
action_choices = ['upload', 'dump']
parser.add_argument('--action', choices=action_choices, default=action_choices[1])
parser.add_argument('--dump-format', required=(action_choices[1] in argv))
This way argparse won't care about the dump format if the dump action wasn't selected

- 606
- 9
- 13
Try this.
Suppose you have a tool that lists, adds and deletes records in a table with the following structure:
id | sitekey | response | status |
---|---|---|---|
1 | 123456 | valid | a |
2 | 234567 | invalid | |
3 | 345678 | invalid | c |
4 | 456789 | valid | d |
And you want to have the following operations and arguments:
- list
- from: optional
- to: optional
- short-response: optional
- add
- sitekey: required
- response: required
- status: optional
- remove
- id: required
Then, you can have a code similar to the following:
import argparse
import sys
operation_choices = ['list', 'add', 'remove']
parser = argparse.ArgumentParser()
parser.add_argument("--operation",
choices = operation_choices,
default = operation_choices[0],
help = "Your help!",
required = True)
# list operation subarguments
if True in list(map(lambda x: operation_choices[0] in x, sys.argv)):
parser.add_argument("--from",
type = int,
default = 1,
help = "Your help!",
required = False)
parser.add_argument("--to",
type = int,
help = "Your help!",
required = False)
parser.add_argument("--short-response",
type = bool,
default = True,
help = "Your help!",
required = False)
# add operation subarguments
if True in list(map(lambda x: operation_choices[1] in x, sys.argv)):
parser.add_argument("--sitekey",
type = str,
help = "Your help!",
required = True)
parser.add_argument("--response",
type = str,
help = "Your help!",
required = True)
parser.add_argument("--status",
type = str,
help = "Your help!",
required = False)
# remove operation subarguments
if True in list(map(lambda x: operation_choices[2] in x, sys.argv)):
parser.add_argument("--id",
type = int,
help = "Your help!",
required = True)
args = parser.parse_args()
# Your operations...
So when you run:
$ python tool.py --operation=list
This run, no required arguments
$ python tool.py --operation=add
usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]
tool.py: error: the following arguments are required: --sitekey, --response
$ python tool.py --operation=remove
usage: tool.py [-h] --operation {list,add,remove} --id ID
tool.py: error: the following arguments are required: --id
$ python tool.py --help
usage: tool.py [-h] --operation {list,add,remove}
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
$ python tool.py --operation=list --help
usage: tool.py [-h] --operation {list,add,remove} [--from FROM] [--to TO] [--short-response SHORT_RESPONSE]
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--from FROM Your help!
--to TO Your help!
--short-response SHORT_RESPONSE
Your help!
$ python tool.py --operation=add --help
usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--sitekey SITEKEY Your help!
--response RESPONSE Your help!
--status STATUS Your help!
$ python tool.py --operation=remove --help
usage: tool.py [-h] --operation {list,add,remove} --id ID
options:
-h, --help show this help message and exit
--operation {list,add,remove}
Your help!
--id ID Your help!