-
-
Save amarao/36327a6f77b86b90c2bca72ba03c9d3a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
import argparse | |
def main(command_line=None): | |
parser = argparse.ArgumentParser('Blame Praise app') | |
parser.add_argument( | |
'--debug', | |
action='store_true', | |
help='Print debug info' | |
) | |
subparsers = parser.add_subparsers(dest='command') | |
blame = subparsers.add_parser('blame', help='blame people') | |
blame.add_argument( | |
'--dry-run', | |
help='do not blame, just pretend', | |
action='store_true' | |
) | |
blame.add_argument('name', nargs='+', help='name(s) to blame') | |
praise = subparsers.add_parser('praise', help='praise someone') | |
praise.add_argument('name', help='name of person to praise') | |
praise.add_argument( | |
'reason', | |
help='what to praise for (optional)', | |
default="no reason", | |
nargs='?' | |
) | |
args = parser.parse_args(command_line) | |
if args.debug: | |
print("debug: " + str(args)) | |
if args.command == 'blame': | |
if args.dry_run: | |
print("Not for real") | |
print("blaming " + ", ".join(args.name)) | |
elif args.command == 'praise': | |
print('praising ' + args.name + ' for ' + args.reason) | |
if __name__ == '__main__': | |
main() |
fixed
@amarao Thanks this is helpful
Thank you! This example was amazing.
This is great - was trying to figure out a way to make a general command-line tool for work and subparser commands fit what I was wanting to do.
Question though - command_line is defined in the function arguments? I'm unsure how this actually works so I'm going to write as I try to think through it. The main() function argument is =None, but this is setting its default value right? And command_line is actually getting set to the arguments given on the command line? How does that work?
Thanks in advance! I just want to solidify my understanding here as much as possible.
There might be a more elegant solution for subparsers by using set_defaults to directly run the desired function:
import argparse
class Test:
def __init__(self) -> None:
pass
def foo(self, args):
print("foo")
def bar(self, args):
print("bar {}".format(args.context))
def main():
test = Test()
parser = argparse.ArgumentParser(description='Foo Bar')
subparsers = parser.add_subparsers(dest='command', help='Commands to run', required=True)
foo = subparsers.add_parser('foo', help='foo some Foos')
foo.set_defaults(func=test.foo)
bar = subparsers.add_parser('bar', help='bar some Bars')
bar.add_argument('context', help='context for bar')
bar.set_defaults(func=test.bar)
args = parser.parse_args()
args.func(args)
if __name__ == '__main__':
main()
edited to include @Cinndy s comment.
Also added required=True to subparser, since there is a known python3 argparse bug, which gives a misleading error message, if you don't provide any arguments.
Traceback (most recent call last):
File "/home/rdo/workspace/repos/test.py", line 30, in <module>
main()
File "/home/rdo/workspace/repos/test.py", line 27, in main
args.func(args)
AttributeError: 'Namespace' object has no attribute 'func'
@luhahn this is great, thank you! One small thing: should the foo definition be foo(self, args)? Thanks again!
@Cinndy indeed, i missed that :)
@luhahn a small thing I don't quite like about this pattern is that it makes all the actual arguments hidden inside Namespace args
instead being explicit. Which also makes it kinda difficult to "retrofit" argparse into a script that has batch of utility functions.
I prefer to keep my actual functions in format similar def func(url, headers=None)
.
This is what I come up with, I know it's not as pretty but works for me. Any input would be appreciated.
import argparse
def minus_one(x):
print(int(x) - 1)
def time(x, y):
print(int(x) * int(y))
def main():
parser = argparse.ArgumentParser(description='Foo Bar')
subparsers = parser.add_subparsers(dest='command', help='Commands to run', required=True)
parser_minus_one = subparsers.add_parser('minusone')
parser_minus_one.add_argument('x', help='X')
parser_minus_one.set_defaults(func=minus_one)
parser_time = subparsers.add_parser('time', help='X times Y')
parser_time.add_argument('x', help='X')
parser_time.add_argument('y', help='Y')
parser_time.set_defaults(func=time)
args = parser.parse_args()
args_ = vars(args).copy()
args_.pop('command', None)
args_.pop('func', None)
args.func(**args_)
if __name__ == '__main__':
main()
typo: "subparsers" is misspelled as
subprasers
.