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) |
This comment has been minimized.
This comment has been minimized.
@alephnull: thanks! I've updated this so that it works with modern Python versions. |
This comment has been minimized.
This comment has been minimized.
This is excellent! Thank you for your post on subparsers; keep finding there is so much more functionality in Minor edit from my own usage: def argument(*names_or_flags, **kwargs):
return names_or_flags, kwargs
def subcommand(*subparser_args, parent=subparsers):
def decorator(func):
parser = parent.add_parser(func.__name__, description=func.__doc__)
for args, kwargs in subparser_args:
parser.add_argument(*args, **kwargs)
parser.set_defaults(func=func)
return decorator
@subcommand(argument('-f', '--filename', help="A thing with a filename"))
def filename(args):
print(args.filename) |
This comment has been minimized.
This comment has been minimized.
Thank you for posting your argparse tutorial . It is helping me to write better command line interfaces with argparse. for instance let's say I want to have an add_contacts subcommand and I want to have arguments for name, address and phone. How would that look? I am able to add a decorator for the name argument, but if try to add multiple decorators I get the error:
any help would be greatly appreciated! |
This comment has been minimized.
This comment has been minimized.
nvm, I figured it out, I wasn't paying close enough attention. I wasn't passing in all of the arguments in one list. It's working for me now. |
This comment has been minimized.
This comment has been minimized.
It's a sensible approach. What's not clear is how to deal with flags that pertain not to a single subcommand but to the entire app. In the example, you've attached the
does not work ( |
This comment has been minimized.
This comment has been minimized.
As written there's currently no way to add an application-wide argument like that, but it shouldn't be too hard to modify things to allow for that. I would think in that case it would be best to define an |
This comment has been minimized.
[ *name_or_flags ]
seems to be invalid syntax now. I replaced it withlist(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.