0

After looking at about a dozen questions, I can't seem to find an answer.

I have a python CLI i've written using argparse. I have a main command that does nothing but regurgitate help text and then 4 subcommands. My boss wants a very specific output for the help text. He has me write it out as a text file and then we use that text file to display the help text.

However, in some circumstances, it STILL outputs parts of the argparse help text.

For example, if I run my program with no subcommands, it just outputs our help text from the file. But if I use "-h" or "--help" it will output our help text, followed by the list of positional and optional arguments and other argparse stuff. We don't want that.

I could use "add_help=False" but we want the user to be able to type -h and still get our help text. If we set add help to false, it will display our help text followed by the error "-h not recognized".

Also doing this does nothing for when the user uses -h after a subcommand. I set help=None and usage is set to my custom help text for each subcommand, but it still shows the boilerplate argparse info at the end.

This is what I want to happen: user types in the main command with no subcommands prints my custom help text and nothing else. The user types the main command, no subcommand, followed by -h/--help and it prints my custom help text and nothing else. User types in the main command, one of the subcommands, followed by -h/--help and it outputs my help text and nothing else. User types the main command, a subcommand, and then wrong arguments or too many/ too few arguments displays my help text and nothing else. Basically I only ever want it to print nothing, or print just my help text.

how do I do that? here is my main function where the parsers and subparsers are all configured:

def main():
    # Import help text from file
    p = Path(__file__).with_name("help.txt")
    with p.open() as file:
        help_text = file.read()

    # Configure the top level Parser
    parser = argparse.ArgumentParser(prog='myprog', description='My program', usage=help_text)
    subparsers = parser.add_subparsers()

    # Create Subparsers to give subcommands
    parser_one = subparsers.add_parser('subcommandone', prog='subcommandone', usage=help_text, help=None)
    parser_one.add_argument('arg1', type=str)
    parser_one.add_argument('-o', '--option1', default='mydefault', type=str)
    parser_two= subparsers.add_parser('subcommandtwo', usage=help_text, help=None, prog='subcommandtwo')
    parser_three= subparsers.add_parser('subcommandthree', usage=help_text, help=None, prog='subcommandthree')
    parser_four= subparsers.add_parser('subcommandfour', usage=help_text, help=None, prog='subcommandfour')

    # Assign subparsers to their respective functions
    parser_one.set_defaults(func=functionone)
    parser_two.set_defaults(func=functiontwo)
    parser_three.set_defaults(func=functionthree)
    parser_four.set_defaults(func=functionfour)
    parser.set_defaults(func=base_case)

    # Parse the arguments and call appropriate functions
    args = parser.parse_args()
    if len(sys.argv) == 1:
        args.func(args, parser)
    else:
        args.func(args)

Any thoughts?

Derek1st
  • 63
  • 6
  • 1
    Use "add_help=False" to turn off the automatic help, and define your own '-h', using `action='help'` or your own custom `Action` class (see `_HelpAction` subclass for implementation ideas). You'll have to do the same for the subparsers. In some cases it may be simpler to catch the `-h` in `sys.argv` before using `parse_args`. – hpaulj Mar 07 '22 at 19:25
  • So it sounds like just catching -h and then printing my text and disabling help altogether might be a wise move, however, it doesn't catch when there's an incorrect amount of arguments. So it sounds like I do need the help function to some degree. Can you tell me wha taction='help' does? – Derek1st Mar 07 '22 at 19:46
  • 1
    Errors like `incorrect amount of arguments` go through `parser.error` and `parser.exit`. `error`, creates an error message that includes the standard `usage` You can specify `usage` when defining the parser (and subparsers). – hpaulj Mar 07 '22 at 20:06

1 Answers1

1

You can use sys.exit() after the help text has been displayed, and before the parsing has begun, to avoid problems with "-h not recognized".

So anywhere before the line

# Parse the arguments and call appropriate functions

add

if len(sys.argv) == 1 or '-h' in sys.argv or '--help' in sys.argv:
    print(help_text)
    sys.exit(1)

In situations where that is not good enough you can subclass argparse.HelpFormatter like so

usage_help_str = 'myscript command [options]'
epilog_str = "More info can be found at https://..."

class Formatter(argparse.HelpFormatter):
    # override methods and stuff

def formatter(prog):
    return Formatter(prog)

parser = argparse.ArgumentParser(formatter_class=formatter, epilog=epilog_str, usage=usage_help_str, add_help=False)

I tried looking around for documentation on subclassing the helpFormatter, but I couldn't find anything. It looks like people are just looking at the source code to figure out how to subclass it.

hostingutilities.com
  • 8,894
  • 3
  • 41
  • 51
  • That seems a sufficient workaround and I'll probably end up using it. But is there really no way just to tell help to shut up? Like, I know the option to completely disable it is there, but then we don't get the usage text like i want. Maybe... maybe if i disabled help entirely and then made my own optional parameter and name it help/h. Hmm. Anyway thank you – Derek1st Mar 07 '22 at 19:28