From 136de7e22fa04ed41fc37b8a6c900cf507db8f26 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 20 Jan 2018 11:54:43 -0500 Subject: Added support for argparse sub-commands when using cmd2 decorators Modified the do_help() method to behave differently for methods which have been decorated with an argparse ArgumentParser. This is so that help will properly deal with sub-command help. Suppose you have a base command "base" which has two sub-commands, "foo" and "bar". Then "help base" will provide very different help text than "help base foo". Slightly tweaked the two argparse decorators to set an attribute in the decorated function's dictionary so that the do_help method can know which functions have an ArgumentParser and which do not. Added a "subcommands.py" example for demonstrating how to create and use subcommands based on argparse and the cmd2 @with_argument_parser decorator. --- examples/subcommands.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 examples/subcommands.py (limited to 'examples/subcommands.py') diff --git a/examples/subcommands.py b/examples/subcommands.py new file mode 100755 index 00000000..347cb61d --- /dev/null +++ b/examples/subcommands.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# coding=utf-8 +"""A simple example demonstrating how to use Argparse to support sub-commands. + + +This example shows an easy way for a single command to have many subcommands, each of which takes different arguments +and provides separate contextual help. +""" +import argparse + +import cmd2 +from cmd2 import with_argument_parser + + +class SubcommandsExample(cmd2.Cmd): + """ Example cmd2 application where we a base command which has a couple subcommands.""" + + def __init__(self): + cmd2.Cmd.__init__(self) + + # sub-command functions for the base command + def foo(self, args): + """foo subcommand of base command""" + print(args.x * args.y) + + def bar(self, args): + """bar sucommand of base command""" + print('((%s))' % args.z) + + # create the top-level parser + base_parser = argparse.ArgumentParser(prog='base') + base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help') + + # create the parser for the "foo" command + parser_foo = base_subparsers.add_parser('foo', help='foo help') + parser_foo.add_argument('-x', type=int, default=1, help='integer') + parser_foo.add_argument('y', type=float, help='float') + parser_foo.set_defaults(func=foo) + + # create the parser for the "bar" command + parser_bar = base_subparsers.add_parser('bar', help='bar help') + parser_bar.add_argument('z', help='string') + parser_bar.set_defaults(func=bar) + + @with_argument_parser(base_parser) + def do_base(self, args): + """Base command help""" + try: + # Call whatever sub-command function was selected + args.func(self, args) + except AttributeError: + # No sub-command was provided, so as called + self.do_help('base') + + +if __name__ == '__main__': + app = SubcommandsExample() + app.cmdloop() -- cgit v1.2.1 From 74ad43dffd9aba2ff17a6e7e2948681745a5a4bf Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 20 Jan 2018 13:30:19 -0500 Subject: Just improved a few comments in the subcommands.py example --- examples/subcommands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'examples/subcommands.py') diff --git a/examples/subcommands.py b/examples/subcommands.py index 347cb61d..7cab74dd 100755 --- a/examples/subcommands.py +++ b/examples/subcommands.py @@ -27,17 +27,17 @@ class SubcommandsExample(cmd2.Cmd): """bar sucommand of base command""" print('((%s))' % args.z) - # create the top-level parser + # create the top-level parser for the base command base_parser = argparse.ArgumentParser(prog='base') base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help') - # create the parser for the "foo" command + # create the parser for the "foo" sub-command parser_foo = base_subparsers.add_parser('foo', help='foo help') parser_foo.add_argument('-x', type=int, default=1, help='integer') parser_foo.add_argument('y', type=float, help='float') parser_foo.set_defaults(func=foo) - # create the parser for the "bar" command + # create the parser for the "bar" sub-command parser_bar = base_subparsers.add_parser('bar', help='bar help') parser_bar.add_argument('z', help='string') parser_bar.set_defaults(func=bar) -- cgit v1.2.1 From 29ef26875664dfa3d70273b321092a83a68d9966 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 20 Jan 2018 15:48:54 -0500 Subject: Tab-completion of subcommand names is now supported --- examples/subcommands.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'examples/subcommands.py') diff --git a/examples/subcommands.py b/examples/subcommands.py index 7cab74dd..eaadbb64 100755 --- a/examples/subcommands.py +++ b/examples/subcommands.py @@ -42,7 +42,10 @@ class SubcommandsExample(cmd2.Cmd): parser_bar.add_argument('z', help='string') parser_bar.set_defaults(func=bar) - @with_argument_parser(base_parser) + # Create a list of subcommand names, which is used to enable tab-completion of sub-commands + subcommands = ['foo', 'bar'] + + @with_argument_parser(base_parser, subcommands) def do_base(self, args): """Base command help""" try: -- cgit v1.2.1 From 5550ab73a91d2834e6bda72eb3889998ad59be51 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 20 Jan 2018 16:48:52 -0500 Subject: Added unit tests for sub-commands --- examples/subcommands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples/subcommands.py') diff --git a/examples/subcommands.py b/examples/subcommands.py index eaadbb64..67b06e18 100755 --- a/examples/subcommands.py +++ b/examples/subcommands.py @@ -21,11 +21,11 @@ class SubcommandsExample(cmd2.Cmd): # sub-command functions for the base command def foo(self, args): """foo subcommand of base command""" - print(args.x * args.y) + self.poutput(args.x * args.y) def bar(self, args): """bar sucommand of base command""" - print('((%s))' % args.z) + self.poutput('((%s))' % args.z) # create the top-level parser for the base command base_parser = argparse.ArgumentParser(prog='base') -- cgit v1.2.1 From c9f7c012bda012b4df7a8c5e853bd5d3e6d99b1b Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sun, 21 Jan 2018 22:24:09 -0500 Subject: Renamed @with_argument_parser decorator to @with_argparser Also: - Reanamed foo and bar subcommand methods to base_foo and base_bar --- examples/subcommands.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'examples/subcommands.py') diff --git a/examples/subcommands.py b/examples/subcommands.py index 67b06e18..e77abc61 100755 --- a/examples/subcommands.py +++ b/examples/subcommands.py @@ -9,7 +9,7 @@ and provides separate contextual help. import argparse import cmd2 -from cmd2 import with_argument_parser +from cmd2 import with_argparser class SubcommandsExample(cmd2.Cmd): @@ -19,11 +19,11 @@ class SubcommandsExample(cmd2.Cmd): cmd2.Cmd.__init__(self) # sub-command functions for the base command - def foo(self, args): + def base_foo(self, args): """foo subcommand of base command""" self.poutput(args.x * args.y) - def bar(self, args): + def base_bar(self, args): """bar sucommand of base command""" self.poutput('((%s))' % args.z) @@ -35,17 +35,17 @@ class SubcommandsExample(cmd2.Cmd): parser_foo = base_subparsers.add_parser('foo', help='foo help') parser_foo.add_argument('-x', type=int, default=1, help='integer') parser_foo.add_argument('y', type=float, help='float') - parser_foo.set_defaults(func=foo) + parser_foo.set_defaults(func=base_foo) # create the parser for the "bar" sub-command parser_bar = base_subparsers.add_parser('bar', help='bar help') parser_bar.add_argument('z', help='string') - parser_bar.set_defaults(func=bar) + parser_bar.set_defaults(func=base_bar) # Create a list of subcommand names, which is used to enable tab-completion of sub-commands subcommands = ['foo', 'bar'] - @with_argument_parser(base_parser, subcommands) + @with_argparser(base_parser, subcommands) def do_base(self, args): """Base command help""" try: -- cgit v1.2.1