CLI subcommands with precise help output
Different ways to create subcommands like in hg or svn with the exact command line interface and help output as shown in Remarks section.
Parsing Command Line arguments covers broader topic of arguments parsing.
Native way (no libraries)
Section titled “Native way (no libraries)”"""usage: sub <command>
commands:
status - show status list - print list"""
import sys
def check(): print("status") return 0
if sys.argv[1:] == ['status']: sys.exit(check())elif sys.argv[1:] == ['list']: print("list")else: print(__doc__.strip())Output without arguments:
usage: sub <command>
commands:
status - show status list - print listPros:
- no deps
- everybody should be able to read that
- complete control over help formatting
argparse (default help formatter)
Section titled “argparse (default help formatter)”import argparseimport sys
def check(): print("status") return 0
parser = argparse.ArgumentParser(prog="sub", add_help=False)subparser = parser.add_subparsers(dest="cmd")
subparser.add_parser('status', help='show status')subparser.add_parser('list', help='print list')
# hack to show help when no arguments suppliedif len(sys.argv) == 1: parser.print_help() sys.exit(0)
args = parser.parse_args()
if args.cmd == 'list': print('list')elif args.cmd == 'status': sys.exit(check())Output without arguments:
usage: sub {status,list} ...
positional arguments: {status,list} status show status list print listPros:
- comes with Python
- option parsing is included
argparse (custom help formatter)
Section titled “argparse (custom help formatter)”Extended version of https://stackoverflow.com/documentation/python/7701/cli-subcommands/25282/argparse-default-help-formatter that fixed help output.
import argparseimport sys
class CustomHelpFormatter(argparse.HelpFormatter): def _format_action(self, action): if type(action) == argparse._SubParsersAction: # inject new class variable for subcommand formatting subactions = action._get_subactions() invocations = [self._format_action_invocation(a) for a in subactions] self._subcommand_max_length = max(len(i) for i in invocations)
if type(action) == argparse._SubParsersAction._ChoicesPseudoAction: # format subcommand help line subcommand = self._format_action_invocation(action) # type: str width = self._subcommand_max_length help_text = "" if action.help: help_text = self._expand_help(action) return " {:{width}} - {}\n".format(subcommand, help_text, width=width)
elif type(action) == argparse._SubParsersAction: # process subcommand help section msg = '\n' for subaction in action._get_subactions(): msg += self._format_action(subaction) return msg else: return super(CustomHelpFormatter, self)._format_action(action)
def check(): print("status") return 0
parser = argparse.ArgumentParser(usage="sub <command>", add_help=False, formatter_class=CustomHelpFormatter)
subparser = parser.add_subparsers(dest="cmd")subparser.add_parser('status', help='show status')subparser.add_parser('list', help='print list')
# custom help messgeparser._positionals.title = "commands"
# hack to show help when no arguments suppliedif len(sys.argv) == 1: parser.print_help() sys.exit(0)
args = parser.parse_args()
if args.cmd == 'list': print('list')elif args.cmd == 'status': sys.exit(check())Output without arguments:
usage: sub <command>
commands:
status - show status list - print listRemarks
Section titled “Remarks”Different ways to create subcommands like in hg or svn with the command line interface shown in the help message:
usage: sub <command>
commands:
status - show status list - print list