Skip to content

Commit 9fee610

Browse files
authored
Merge pull request #568 from python-cmd2/sub-command
Using sub-command instead of subcommand where possible to be consistent with argparse
2 parents 49cbec9 + 734796b commit 9fee610

11 files changed

+77
-70
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
have been deleted
3232
* The new application lifecycle hook system allows for registration of callbacks to be called at various points
3333
in the lifecycle and is more powerful and flexible than the previous system
34-
* ``alias`` is now a command with subcommands to create, list, and delete aliases. Therefore its syntax
34+
* ``alias`` is now a command with sub-commands to create, list, and delete aliases. Therefore its syntax
3535
has changed. All current alias commands in startup scripts or transcripts will break with this release.
3636
* `unalias` was deleted since ``alias delete`` replaced it
3737

cmd2/argparse_completer.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,13 @@ def set_custom_message(self, custom_message: str='') -> None:
917917
self._custom_error_message = custom_message
918918
# End cmd2 customization
919919

920+
def add_subparsers(self, **kwargs):
921+
"""Custom override. Sets a default title if one was not given."""
922+
if 'title' not in kwargs:
923+
kwargs['title'] = 'sub-commands'
924+
925+
return super().add_subparsers(**kwargs)
926+
920927
def error(self, message: str) -> None:
921928
"""Custom error override. Allows application to control the error being displayed by argparse"""
922929
if len(self._custom_error_message) > 0:

cmd2/cmd2.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,7 +2204,7 @@ def _cmdloop(self) -> bool:
22042204

22052205
return stop
22062206

2207-
# ----- Alias subcommand functions -----
2207+
# ----- Alias sub-command functions -----
22082208

22092209
def alias_create(self, args: argparse.Namespace):
22102210
"""Create or overwrite an alias"""
@@ -2267,8 +2267,8 @@ def alias_list(self, args: argparse.Namespace):
22672267
" macro")
22682268
alias_parser = ACArgumentParser(description=alias_description, epilog=alias_epilog, prog='alias')
22692269

2270-
# Add subcommands to alias
2271-
alias_subparsers = alias_parser.add_subparsers(title='sub-commands')
2270+
# Add sub-commands to alias
2271+
alias_subparsers = alias_parser.add_subparsers()
22722272

22732273
# alias -> create
22742274
alias_create_help = "create or overwrite an alias"
@@ -2326,13 +2326,13 @@ def do_alias(self, args: argparse.Namespace):
23262326
"""Manage aliases"""
23272327
func = getattr(args, 'func', None)
23282328
if func is not None:
2329-
# Call whatever subcommand function was selected
2329+
# Call whatever sub-command function was selected
23302330
func(self, args)
23312331
else:
2332-
# No subcommand was provided, so call help
2332+
# No sub-command was provided, so call help
23332333
self.do_help('alias')
23342334

2335-
# ----- Macro subcommand functions -----
2335+
# ----- Macro sub-command functions -----
23362336

23372337
def macro_create(self, args: argparse.Namespace):
23382338
"""Create or overwrite a macro"""
@@ -2445,8 +2445,8 @@ def macro_list(self, args: argparse.Namespace):
24452445
" alias")
24462446
macro_parser = ACArgumentParser(description=macro_description, epilog=macro_epilog, prog='macro')
24472447

2448-
# Add subcommands to macro
2449-
macro_subparsers = macro_parser.add_subparsers(title='sub-commands')
2448+
# Add sub-commands to macro
2449+
macro_subparsers = macro_parser.add_subparsers()
24502450

24512451
# macro -> create
24522452
macro_create_help = "create or overwrite a macro"
@@ -2527,10 +2527,10 @@ def do_macro(self, args: argparse.Namespace):
25272527
"""Manage macros"""
25282528
func = getattr(args, 'func', None)
25292529
if func is not None:
2530-
# Call whatever subcommand function was selected
2530+
# Call whatever sub-command function was selected
25312531
func(self, args)
25322532
else:
2533-
# No subcommand was provided, so call help
2533+
# No sub-command was provided, so call help
25342534
self.do_help('macro')
25352535

25362536
def complete_help_command(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
@@ -2551,7 +2551,7 @@ def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: in
25512551
if not tokens:
25522552
return []
25532553

2554-
# Must have at least 3 args for 'help command subcommand'
2554+
# Must have at least 3 args for 'help command sub-command'
25552555
if len(tokens) < 3:
25562556
return []
25572557

@@ -2580,7 +2580,7 @@ def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: in
25802580

25812581
setattr(help_parser.add_argument('command', help="command to retrieve help for", nargs="?"),
25822582
ACTION_ARG_CHOICES, ('complete_help_command',))
2583-
setattr(help_parser.add_argument('subcommand', help="subcommand to retrieve help for",
2583+
setattr(help_parser.add_argument('subcommand', help="sub-command to retrieve help for",
25842584
nargs=argparse.REMAINDER),
25852585
ACTION_ARG_CHOICES, ('complete_help_subcommand',))
25862586
help_parser.add_argument('-v', '--verbose', action='store_true',

cmd2/pyscript_bridge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def __dir__(self):
8787
return commands
8888

8989
def __getattr__(self, item: str):
90-
"""Search for a subcommand matching this item and update internal state to track the traversal"""
90+
"""Search for a sub-command matching this item and update internal state to track the traversal"""
9191
# look for sub-command under the current command/sub-command layer
9292
for action in self.__current_subcommand_parser._actions:
9393
if not action.option_strings and isinstance(action, argparse._SubParsersAction):

examples/subcommands.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env python3
22
# coding=utf-8
3-
"""A simple example demonstrating how to use Argparse to support subcommands.
3+
"""A simple example demonstrating how to use Argparse to support sub-commands.
44
55
6-
This example shows an easy way for a single command to have many subcommands, each of which takes different arguments
6+
This example shows an easy way for a single command to have many sub-commands, each of which takes different arguments
77
and provides separate contextual help.
88
"""
99
import argparse
@@ -13,15 +13,15 @@
1313

1414
# create the top-level parser for the base command
1515
base_parser = argparse.ArgumentParser(prog='base')
16-
base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help')
16+
base_subparsers = base_parser.add_subparsers(title='sub-commands', help='sub-command help')
1717

18-
# create the parser for the "foo" subcommand
18+
# create the parser for the "foo" sub-command
1919
parser_foo = base_subparsers.add_parser('foo', help='foo help')
2020
parser_foo.add_argument('-x', type=int, default=1, help='integer')
2121
parser_foo.add_argument('y', type=float, help='float')
2222
parser_foo.add_argument('input_file', type=str, help='Input File')
2323

24-
# create the parser for the "bar" subcommand
24+
# create the parser for the "bar" sub-command
2525
parser_bar = base_subparsers.add_parser('bar', help='bar help')
2626

2727
bar_subparsers = parser_bar.add_subparsers(title='layer3', help='help for 3rd layer of commands')
@@ -31,7 +31,7 @@
3131
bar_subparsers.add_parser('artichoke', help='artichoke help')
3232
bar_subparsers.add_parser('cranberries', help='cranberries help')
3333

34-
# create the parser for the "sport" subcommand
34+
# create the parser for the "sport" sub-command
3535
parser_sport = base_subparsers.add_parser('sport', help='sport help')
3636
sport_arg = parser_sport.add_argument('sport', help='Enter name of a sport')
3737
setattr(sport_arg, 'arg_choices', sport_item_strs)
@@ -40,15 +40,15 @@
4040
# create the top-level parser for the alternate command
4141
# The alternate command doesn't provide its own help flag
4242
base2_parser = argparse.ArgumentParser(prog='alternate', add_help=False)
43-
base2_subparsers = base2_parser.add_subparsers(title='subcommands', help='subcommand help')
43+
base2_subparsers = base2_parser.add_subparsers(title='sub-commands', help='sub-command help')
4444

45-
# create the parser for the "foo" subcommand
45+
# create the parser for the "foo" sub-command
4646
parser_foo2 = base2_subparsers.add_parser('foo', help='foo help')
4747
parser_foo2.add_argument('-x', type=int, default=1, help='integer')
4848
parser_foo2.add_argument('y', type=float, help='float')
4949
parser_foo2.add_argument('input_file', type=str, help='Input File')
5050

51-
# create the parser for the "bar" subcommand
51+
# create the parser for the "bar" sub-command
5252
parser_bar2 = base2_subparsers.add_parser('bar', help='bar help')
5353

5454
bar2_subparsers = parser_bar2.add_subparsers(title='layer3', help='help for 3rd layer of commands')
@@ -58,34 +58,34 @@
5858
bar2_subparsers.add_parser('artichoke', help='artichoke help')
5959
bar2_subparsers.add_parser('cranberries', help='cranberries help')
6060

61-
# create the parser for the "sport" subcommand
61+
# create the parser for the "sport" sub-command
6262
parser_sport2 = base2_subparsers.add_parser('sport', help='sport help')
6363
sport2_arg = parser_sport2.add_argument('sport', help='Enter name of a sport')
6464
setattr(sport2_arg, 'arg_choices', sport_item_strs)
6565

6666

6767
class SubcommandsExample(cmd2.Cmd):
6868
"""
69-
Example cmd2 application where we a base command which has a couple subcommands
70-
and the "sport" subcommand has tab completion enabled.
69+
Example cmd2 application where we a base command which has a couple sub-commands
70+
and the "sport" sub-command has tab completion enabled.
7171
"""
7272
def __init__(self):
7373
super().__init__()
7474

75-
# subcommand functions for the base command
75+
# sub-command functions for the base command
7676
def base_foo(self, args):
77-
"""foo subcommand of base command"""
77+
"""foo sub-command of base command"""
7878
self.poutput(args.x * args.y)
7979

8080
def base_bar(self, args):
81-
"""bar subcommand of base command"""
81+
"""bar sub-command of base command"""
8282
self.poutput('((%s))' % args.z)
8383

8484
def base_sport(self, args):
85-
"""sport subcommand of base command"""
85+
"""sport sub-command of base command"""
8686
self.poutput('Sport is {}'.format(args.sport))
8787

88-
# Set handler functions for the subcommands
88+
# Set handler functions for the sub-commands
8989
parser_foo.set_defaults(func=base_foo)
9090
parser_bar.set_defaults(func=base_bar)
9191
parser_sport.set_defaults(func=base_sport)
@@ -95,21 +95,21 @@ def do_base(self, args):
9595
"""Base command help"""
9696
func = getattr(args, 'func', None)
9797
if func is not None:
98-
# Call whatever subcommand function was selected
98+
# Call whatever sub-command function was selected
9999
func(self, args)
100100
else:
101-
# No subcommand was provided, so call help
101+
# No sub-command was provided, so call help
102102
self.do_help('base')
103103

104104
@cmd2.with_argparser(base2_parser)
105105
def do_alternate(self, args):
106106
"""Alternate command help"""
107107
func = getattr(args, 'func', None)
108108
if func is not None:
109-
# Call whatever subcommand function was selected
109+
# Call whatever sub-command function was selected
110110
func(self, args)
111111
else:
112-
# No subcommand was provided, so call help
112+
# No sub-command was provided, so call help
113113
self.do_help('alternate')
114114

115115

examples/tab_autocomp_dynamic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def query_actors() -> List[str]:
2525

2626

2727
class TabCompleteExample(cmd2.Cmd):
28-
""" Example cmd2 application where we a base command which has a couple subcommands."""
28+
""" Example cmd2 application where we a base command which has a couple sub-commands."""
2929

3030
CAT_AUTOCOMPLETE = 'AutoComplete Examples'
3131

@@ -225,7 +225,7 @@ def _do_vid_media_shows(self, args) -> None:
225225
@cmd2.with_category(CAT_AUTOCOMPLETE)
226226
@cmd2.with_argparser(video_parser)
227227
def do_video(self, args):
228-
"""Video management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
228+
"""Video management command demonstrates multiple layers of sub-commands being handled by AutoCompleter"""
229229
func = getattr(args, 'func', None)
230230
if func is not None:
231231
# Call whatever subcommand function was selected

examples/tab_autocompletion.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def query_actors() -> List[str]:
2525

2626

2727
class TabCompleteExample(cmd2.Cmd):
28-
""" Example cmd2 application where we a base command which has a couple subcommands."""
28+
""" Example cmd2 application where we a base command which has a couple sub-commands."""
2929

3030
CAT_AUTOCOMPLETE = 'AutoComplete Examples'
3131

@@ -275,7 +275,7 @@ def _do_vid_media_shows(self, args) -> None:
275275
@cmd2.with_category(CAT_AUTOCOMPLETE)
276276
@cmd2.with_argparser(video_parser)
277277
def do_video(self, args):
278-
"""Video management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
278+
"""Video management command demonstrates multiple layers of sub-commands being handled by AutoCompleter"""
279279
func = getattr(args, 'func', None)
280280
if func is not None:
281281
# Call whatever subcommand function was selected
@@ -356,7 +356,7 @@ def _do_media_shows(self, args) -> None:
356356
@cmd2.with_category(CAT_AUTOCOMPLETE)
357357
@cmd2.with_argparser(media_parser)
358358
def do_media(self, args):
359-
"""Media management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
359+
"""Media management command demonstrates multiple layers of sub-commands being handled by AutoCompleter"""
360360
func = getattr(args, 'func', None)
361361
if func is not None:
362362
# Call whatever subcommand function was selected
@@ -457,7 +457,7 @@ def _filter_episodes(self, text, line, begidx, endidx, show_db, user_lib):
457457
@cmd2.with_category(CAT_AUTOCOMPLETE)
458458
@cmd2.with_argparser(library_parser)
459459
def do_library(self, args):
460-
"""Media management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
460+
"""Media management command demonstrates multiple layers of sub-commands being handled by AutoCompleter"""
461461
func = getattr(args, 'func', None)
462462
if func is not None:
463463
# Call whatever subcommand function was selected

examples/tab_completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313

1414
class TabCompleteExample(cmd2.Cmd):
15-
""" Example cmd2 application where we a base command which has a couple subcommands."""
15+
""" Example cmd2 application where we a base command which has a couple sub-commands."""
1616

1717
def __init__(self):
1818
super().__init__()

tests/test_argparse.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,31 +179,31 @@ def test_arglist_decorator_twice(argparse_app):
179179

180180

181181
class SubcommandApp(cmd2.Cmd):
182-
""" Example cmd2 application where we a base command which has a couple subcommands."""
182+
""" Example cmd2 application where we a base command which has a couple sub-commands."""
183183

184184
def __init__(self):
185185
cmd2.Cmd.__init__(self)
186186

187-
# subcommand functions for the base command
187+
# sub-command functions for the base command
188188
def base_foo(self, args):
189-
"""foo subcommand of base command"""
189+
"""foo sub-command of base command"""
190190
self.poutput(args.x * args.y)
191191

192192
def base_bar(self, args):
193-
"""bar subcommand of base command"""
193+
"""bar sub-command of base command"""
194194
self.poutput('((%s))' % args.z)
195195

196196
# create the top-level parser for the base command
197197
base_parser = argparse.ArgumentParser(prog='base')
198-
base_subparsers = base_parser.add_subparsers(title='subcommands', help='subcommand help')
198+
base_subparsers = base_parser.add_subparsers(title='sub-commands', help='sub-command help')
199199

200-
# create the parser for the "foo" subcommand
200+
# create the parser for the "foo" sub-command
201201
parser_foo = base_subparsers.add_parser('foo', help='foo help')
202202
parser_foo.add_argument('-x', type=int, default=1, help='integer')
203203
parser_foo.add_argument('y', type=float, help='float')
204204
parser_foo.set_defaults(func=base_foo)
205205

206-
# create the parser for the "bar" subcommand
206+
# create the parser for the "bar" sub-command
207207
parser_bar = base_subparsers.add_parser('bar', help='bar help')
208208
parser_bar.add_argument('z', help='string')
209209
parser_bar.set_defaults(func=base_bar)
@@ -213,10 +213,10 @@ def do_base(self, args):
213213
"""Base command help"""
214214
func = getattr(args, 'func', None)
215215
if func is not None:
216-
# Call whatever subcommand function was selected
216+
# Call whatever sub-command function was selected
217217
func(self, args)
218218
else:
219-
# No subcommand was provided, so call help
219+
# No sub-command was provided, so call help
220220
self.do_help('base')
221221

222222
@pytest.fixture

0 commit comments

Comments
 (0)