Home > Net >  python::argparse: How to print help level-by-level?
python::argparse: How to print help level-by-level?

Time:11-27

I have a multi-level command line program like 'git'.

my_cli service action --options

I want to show help message level-by-level, AND I don't want the user to explicitly type "-h" or "--help".

For example,

$ my_cli     <== display help of all services
$ my_cli service1    <== display help for service1 only
$ my_cli service1 action1    <== display help for service1/action1 only

The code looks like below.

import argparse

argument_parser = argparse.ArgumentParser(description="my_cli")
root_parsers = argument_parser.add_subparsers(title="service", dest="service")

service1_parsers = root_parsers.add_parser("service1", help="service1").add_subparsers(title="action", dest="action")
service2_parsers = root_parsers.add_parser("service2", help="service2").add_subparsers(title="action", dest="action")

service1_action1_parser = service1_parsers.add_parser("action1", help="action1")
service1_action1_parser.add_argument("-a", "--address", required=True, help="address or hostname of the server")
...

args = argument_parser.parse_args()
if (args.service is None):
    argument_parser.print_help()
    exit(1)
elif (args.action is None):
    if (args.service == "service1"):
        service1_parsers.print_help()    <== This doesn't work.
        exit(1)
    ...
else:
    if (args.service == "service1") AND (args.action == "action1"):
        service1_action1_parser.print_help()    <== This doesn't work.
        exit(1)
    ...

CodePudding user response:

In your example, calling my_cli service1 action1 does display some sort of help message, but it's more of a usage message since you've marked the --address argument as required, which failed the parser validation. The usage message is

usage: test3.py service1 action1 [-h] -a ADDRESS
test3.py service1 action1: error: the following arguments are required: -a/--address

The issue in your example for calling my_cli service1 not showing the help message is that you're calling print_help() on a subparser when you should've called it on a parser instead. Something like this should work.

import argparse

argument_parser = argparse.ArgumentParser(description="my_cli")
root_parsers = argument_parser.add_subparsers(title="service", dest="service")

service1_parser = root_parsers.add_parser("service1", help="service1")
service1_subparsers = service1_parser.add_subparsers(title="action", dest="action")
service2_parser = root_parsers.add_parser("service2", help="service2")
service2_subparsers = service2_parser.add_subparsers(title="action", dest="action")

service1_action1_parser = service1_subparsers.add_parser("action1", help="action1")
# I removed the required=True here for the purposes of showing how to get the help message
service1_action1_parser.add_argument("-a", "--address", help="address or hostname of the server")

args = argument_parser.parse_args()

if args.service is None:
    argument_parser.print_help()
    exit(1)
elif args.action is None:
    if args.service == "service1":
        # call print_help() on a parser instead of a subparser
        service1_parser.print_help()
    elif args.service == "service2":
        service2_parser.print_help()
    exit(1)
elif args.service == "service1" and args.action == "action1":
    service1_action1_parser.print_help()
    exit(1)

The outputs I get are:

$ ./my_cli
usage: my_cli [-h] {service1,service2} ...

my_cli

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

service:
  {service1,service2}
    service1           service1
    service2           service2
$ ./my_cli service1
usage: my_cli service1 [-h] {action1} ...

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

action:
  {action1}
    action1   action1
$ ./my_cli service1 action1
usage: my_cli service1 action1 [-h] [-a ADDRESS]

optional arguments:
  -h, --help            show this help message and exit
  -a ADDRESS, --address ADDRESS
                        address or hostname of the server
  • Related