Instantly share code, notes, and snippets.

@mivade /cli.py
Last active Dec 19, 2018

Embed
What would you like to do?
Using a decorator to simplify subcommand creation with argparse
from argparse import ArgumentParser
cli = ArgumentParser()
subparsers = cli.add_subparsers(dest="subcommand")
def argument(*name_or_flags, **kwargs):
"""Convenience function to properly format arguments to pass to the
subcommand decorator.
"""
return (list(name_or_flags), kwargs)
def subcommand(args=[], parent=subparsers):
"""Decorator to define a new subcommand in a sanity-preserving way.
The function will be stored in the ``func`` variable when the parser
parses arguments so that it can be called directly like so::
args = cli.parse_args()
args.func(args)
Usage example::
@subcommand([argument("-d", help="Enable debug mode", action="store_true")])
def subcommand(args):
print(args)
Then on the command line::
$ python cli.py subcommand -d
"""
def decorator(func):
parser = parent.add_parser(func.__name__, description=func.__doc__)
for arg in args:
parser.add_argument(*arg[0], **arg[1])
parser.set_defaults(func=func)
return decorator
@subcommand()
def nothing(args):
print("Nothing special!")
@subcommand([argument("-d", help="Debug mode", action="store_true")])
def test(args):
print(args)
@subcommand([argument("-f", "--filename", help="A thing with a filename")])
def filename(args):
print(args.filename)
@subcommand([argument("name", help="Name")])
def name(args):
print(args.name)
if __name__ == "__main__":
args = cli.parse_args()
if args.subcommand is None:
cli.print_help()
else:
args.func(args)
@alephnull

This comment has been minimized.

Copy link

alephnull commented Jun 15, 2018

[ *name_or_flags ] seems to be invalid syntax now. I replaced it with list(name_or_flags).

This is a nice way of dealing with argparse. Makes it more like click. I spent half a day futzing with docopt for a subcommand driven CLI before implementing it your way.

@mivade

This comment has been minimized.

Copy link
Owner

mivade commented Aug 14, 2018

@alephnull: thanks! I've updated this so that it works with modern Python versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment