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.
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()
3 Comments
print require ()? Why is it not used above? It errors when I run the example above because print tells me the syntax should be print('something') rather than print 'something'. Is this an environment specific quirk? (I am running in Windows)print worked like that in Python 2, this answer is from 2012 when Python 2 was still common.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
3 Comments
argparse does not allow multiple subparsers.dump dumpsubcommand1 , dump subcommand2, upload subcommand3 etc..?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.')
3 Comments
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
1 Comment
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!
--action=dump-csvor--action=dump-some-other-format? This would alleviate the problem of having "required options" entirely.