Python argparse: Make at least one argument required

Viewed 65048

I've been using argparse for a Python program that can -process, -upload or both:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload',  action='store_true')
args = parser.parse_args()

The program is meaningless without at least one parameter. How can I configure argparse to force at least one parameter to be chosen?

UPDATE:

Following the comments: What's the Pythonic way to parametrize a program with at least one option?

13 Answers

This achieves the purpose and this will also be relfected in the argparse autogenerated --help output, which is imho what most sane programmers want (also works with optional arguments):

parser.add_argument(
    'commands',
    nargs='+',                      # require at least 1
    choices=['process', 'upload'],  # restrict the choice
    help='commands to execute'
)

Official docs on this: https://docs.python.org/3/library/argparse.html#choices

Maybe use sub-parsers?

import argparse

parser = argparse.ArgumentParser(description='Log archiver arguments.')
subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
parser_process = subparsers.add_parser('process', help='Process logs')
parser_upload = subparsers.add_parser('upload', help='Upload logs')
args = parser.parse_args()

print("Subparser: ", args.subparser_name)

Now --help shows:

$ python /tmp/aaa.py --help
usage: aaa.py [-h] {process,upload} ...

Log archiver arguments.

positional arguments:
  {process,upload}  sub-command help
    process         Process logs
    upload          Upload logs

optional arguments:
  -h, --help        show this help message and exit
$ python /tmp/aaa.py
usage: aaa.py [-h] {process,upload} ...
aaa.py: error: too few arguments
$ python3 /tmp/aaa.py upload
Subparser:  upload

You can add additional options to these sub-parsers as well. Also instead of using that dest='subparser_name' you can also bind functions to be directly called on given sub-command (see docs).

For cases like

parser.add_argument("--a")
parser.add_argument("--b")

We can use the following

if not args.a and not args.b:
    parser.error("One of --a or --b must be present")

Using

    parser = argparse.ArgumentParser(description='Log archiver arguments.')
    parser.add_argument('-process', action='store_true')
    parser.add_argument('-upload',  action='store_true')
    args = parser.parse_args()

Maybe try:

    if len([False for arg in vars(args) if vars(args)[arg]]) == 0: 
        parsers.print_help()
        exit(-1)

At least this is what I just used; hopefully this helps someone in the future!

Related