Skip to content

Commit 9bda565

Browse files
committed
Fixed enable_category() and disable_category() to support DEFAULT_CATEGORY.
Removed concept of undocumented commands.
1 parent 4c7688b commit 9bda565

File tree

7 files changed

+155
-133
lines changed

7 files changed

+155
-133
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ prompt is displayed.
7676
- Removed `Cmd.doc_header` and the `with_default_category` decorator. Help categorization is now
7777
driven by the `DEFAULT_CATEGORY` class variable (see **Simplified command categorization** in
7878
the Enhancements section below for details).
79+
- Removed `Cmd.undoc_header` since all commands are now considered categorized.
7980
- Enhancements
8081
- New `cmd2.Cmd` parameters
8182
- **auto_suggest**: (boolean) if `True`, provide fish shell style auto-suggestions. These

cmd2/cmd2.py

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,6 @@ class Cmd:
344344
# Header for table listing help topics not related to a command.
345345
MISC_HEADER: ClassVar[str] = "Miscellaneous Help Topics"
346346

347-
# Header for table listing commands that have no help info.
348-
UNDOC_HEADER: ClassVar[str] = "Undocumented Commands"
349-
350347
def __init__(
351348
self,
352349
completekey: str | None = None,
@@ -868,7 +865,7 @@ def register_command_set(self, cmdset: CommandSet) -> None:
868865
self._cmd_to_command_sets[command] = cmdset
869866

870867
# If this command is in a disabled category, then disable it
871-
command_category = getattr(command_method, constants.CMD_ATTR_HELP_CATEGORY, None)
868+
command_category = self._get_command_category(command_method)
872869
if command_category in self.disabled_categories:
873870
message_to_print = self.disabled_categories[command_category]
874871
self.disable_command(command, message_to_print)
@@ -3338,6 +3335,23 @@ def cmd_func(self, command: str) -> CommandFunc | None:
33383335
func = getattr(self, func_name, None)
33393336
return cast(CommandFunc, func) if callable(func) else None
33403337

3338+
def _get_command_category(self, func: CommandFunc) -> str:
3339+
"""Determine the category for a command.
3340+
3341+
:param func: the do_* function implementing the command
3342+
:return: category name
3343+
"""
3344+
# Check if the command function has a category.
3345+
if hasattr(func, constants.CMD_ATTR_HELP_CATEGORY):
3346+
category: str = getattr(func, constants.CMD_ATTR_HELP_CATEGORY)
3347+
3348+
# Otherwise get the category from its defining class.
3349+
else:
3350+
defining_cls = get_defining_class(func)
3351+
category = getattr(defining_cls, 'DEFAULT_CATEGORY', self.DEFAULT_CATEGORY)
3352+
3353+
return category
3354+
33413355
def onecmd(self, statement: Statement | str, *, add_to_history: bool = True) -> bool:
33423356
"""Execute the actual do_* method for a command.
33433357
@@ -4214,12 +4228,11 @@ def complete_help_subcommands(
42144228
completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self)
42154229
return completer.complete_subcommand_help(text, line, begidx, endidx, arg_tokens['subcommands'])
42164230

4217-
def _build_command_info(self) -> tuple[dict[str, list[str]], list[str], list[str]]:
4231+
def _build_command_info(self) -> tuple[dict[str, list[str]], list[str]]:
42184232
"""Categorizes and sorts visible commands and help topics for display.
42194233
42204234
:return: tuple containing:
42214235
- dictionary mapping category names to lists of command names
4222-
- list of undocumented command names
42234236
- list of help topic names that are not also commands
42244237
"""
42254238
# Get a sorted list of help topics
@@ -4228,36 +4241,18 @@ def _build_command_info(self) -> tuple[dict[str, list[str]], list[str], list[str
42284241
# Get a sorted list of visible command names
42294242
visible_commands = sorted(self.get_visible_commands(), key=utils.DEFAULT_STR_SORT_KEY)
42304243
cmds_cats: dict[str, list[str]] = {}
4231-
cmds_undoc: list[str] = []
42324244

42334245
for command in visible_commands:
4234-
func = cast(CommandFunc, self.cmd_func(command))
4235-
has_help_func = False
4236-
has_parser = func in self._command_parsers
4237-
4246+
# Prevent the command from showing as both a command and help topic in the output
42384247
if command in help_topics:
4239-
# Prevent the command from showing as both a command and help topic in the output
42404248
help_topics.remove(command)
42414249

4242-
# Non-argparse commands can have help_functions for their documentation
4243-
has_help_func = not has_parser
4244-
4245-
# Determine the category
4246-
category: str | None = None
4247-
4248-
if hasattr(func, constants.CMD_ATTR_HELP_CATEGORY):
4249-
category = getattr(func, constants.CMD_ATTR_HELP_CATEGORY)
4250-
elif func.__doc__ or has_help_func or has_parser:
4251-
defining_cls = get_defining_class(func)
4252-
category = getattr(defining_cls, 'DEFAULT_CATEGORY', self.DEFAULT_CATEGORY)
4253-
4254-
# Store the command
4255-
if category is not None:
4256-
cmds_cats.setdefault(category, []).append(command)
4257-
else:
4258-
cmds_undoc.append(command)
4250+
# Store the command within its category
4251+
func = cast(CommandFunc, self.cmd_func(command))
4252+
category = self._get_command_category(func)
4253+
cmds_cats.setdefault(category, []).append(command)
42594254

4260-
return cmds_cats, cmds_undoc, help_topics
4255+
return cmds_cats, help_topics
42614256

42624257
@classmethod
42634258
def _build_help_parser(cls) -> Cmd2ArgumentParser:
@@ -4290,7 +4285,7 @@ def do_help(self, args: argparse.Namespace) -> None:
42904285
self.last_result = True
42914286

42924287
if not args.command or args.verbose:
4293-
cmds_cats, cmds_undoc, help_topics = self._build_command_info()
4288+
cmds_cats, help_topics = self._build_command_info()
42944289

42954290
if self.doc_leader:
42964291
self.poutput()
@@ -4311,11 +4306,10 @@ def do_help(self, args: argparse.Namespace) -> None:
43114306
self._print_documented_command_topics(category, commands, args.verbose)
43124307
previous_table_printed = bool(commands) and args.verbose
43134308

4314-
if previous_table_printed and (help_topics or cmds_undoc):
4309+
if previous_table_printed and help_topics:
43154310
self.poutput()
43164311

43174312
self.print_topics(self.MISC_HEADER, help_topics, 15, 80)
4318-
self.print_topics(self.UNDOC_HEADER, cmds_undoc, 15, 80)
43194313

43204314
else:
43214315
# Getting help for a specific command
@@ -5622,7 +5616,7 @@ def enable_category(self, category: str) -> None:
56225616

56235617
for cmd_name in list(self.disabled_commands):
56245618
func = self.disabled_commands[cmd_name].command_function
5625-
if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
5619+
if self._get_command_category(func) == category:
56265620
self.enable_command(cmd_name)
56275621

56285622
del self.disabled_categories[category]
@@ -5683,8 +5677,8 @@ def disable_category(self, category: str, message_to_print: str) -> None:
56835677
all_commands = self.get_all_commands()
56845678

56855679
for cmd_name in all_commands:
5686-
func = self.cmd_func(cmd_name)
5687-
if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
5680+
func = cast(CommandFunc, self.cmd_func(cmd_name))
5681+
if self._get_command_category(func) == category:
56885682
self.disable_command(cmd_name, message_to_print)
56895683

56905684
self.disabled_categories[category] = message_to_print

docs/features/initialization.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ The `cmd2.Cmd` class provides several class-level variables that can be overridd
2424
- **DEFAULT_EDITOR**: The default editor program used by the `edit` command.
2525
- **DEFAULT_PROMPT**: The default prompt string. (Default: `"(Cmd) "`)
2626
- **MISC_HEADER**: Header for the help section listing miscellaneous help topics. (Default: `"Miscellaneous Help Topics"`)
27-
- **UNDOC_HEADER**: Header for the help section listing undocumented commands. (Default: `"Undocumented Commands"`)
2827

2928
## Cmd instance attributes
3029

examples/default_categories.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ def do_shortcuts(self, _: argparse.Namespace) -> None:
5959
"""Overriding with @with_category(cmd2.Cmd.DEFAULT_CATEGORY) keeps it cmd2's category."""
6060
super().do_shortcuts("")
6161

62-
def do_undocumented(self, _: cmd2.Statement) -> None:
63-
# This has no docstring and no help function, so it stays in "Undocumented Commands"
64-
pass
65-
6662

6763
if __name__ == '__main__':
6864
import sys

tests/test_categories.py

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,13 @@ class CategoryCmd(cmd2.Cmd):
3333

3434
DEFAULT_CATEGORY = "CategoryCmd Commands"
3535

36-
def do_documented(self, _: cmd2.Statement) -> None:
37-
"""This function has a docstring.
36+
def do_cmd_command(self, _: cmd2.Statement) -> None:
37+
"""The cmd command.
3838
3939
Since this class DOES define its own DEFAULT_CATEGORY,
4040
this command will show in CategoryCmd.DEFAULT_CATEGORY
4141
"""
4242

43-
# This function has no docstring and does not use argparse.
44-
# Therefore it will be uncategorized.
45-
def do_undocumented(self, _: cmd2.Statement) -> None:
46-
pass
47-
4843
@with_argparser(Cmd2ArgumentParser(description="Overridden quit command"))
4944
def do_quit(self, _: argparse.Namespace) -> None:
5045
"""This function overrides the cmd2.Cmd quit command.
@@ -62,17 +57,15 @@ def do_shortcuts(self, _: argparse.Namespace) -> None:
6257
cmd2.Cmd.DEFAULT_CATEGORY for the parent class.
6358
"""
6459

65-
# This function has no docstring but it has a help function
66-
# so it will be in CategoryCmd.DEFAULT_CATEGORY
67-
def do_helpless(self, _: cmd2.Statement) -> None:
68-
pass
60+
def do_has_help_func(self, _: cmd2.Statement) -> None:
61+
"""This command has a help function."""
6962

70-
def help_helpless(self) -> None:
71-
"""Help function for the helpless command."""
72-
self.poutput("The function has help.")
63+
def help_has_help_func(self) -> None:
64+
"""Help function for the has_help_func command."""
65+
self.poutput("has_help_func help text.")
7366

7467
def help_coding(self) -> None:
75-
"""This is help function not tied to a command.
68+
"""This help function not tied to a command.
7669
7770
It will be in help topics.
7871
"""
@@ -81,19 +74,18 @@ def help_coding(self) -> None:
8174

8275
def test_no_category_cmd() -> None:
8376
app = NoCategoryCmd()
84-
cmds_cats, _cmds_undoc, _help_topics = app._build_command_info()
77+
cmds_cats, _help_topics = app._build_command_info()
8578
assert "inherit" in cmds_cats[cmd2.Cmd.DEFAULT_CATEGORY]
8679

8780

8881
def test_category_cmd() -> None:
8982
app = CategoryCmd()
90-
cmds_cats, cmds_undoc, help_topics = app._build_command_info()
83+
cmds_cats, help_topics = app._build_command_info()
9184

92-
assert "documented" in cmds_cats[CategoryCmd.DEFAULT_CATEGORY]
93-
assert "undocumented" in cmds_undoc
85+
assert "cmd_command" in cmds_cats[CategoryCmd.DEFAULT_CATEGORY]
9486
assert "quit" in cmds_cats[CategoryCmd.DEFAULT_CATEGORY]
9587
assert "shortcuts" in cmds_cats[cmd2.Cmd.DEFAULT_CATEGORY]
96-
assert "helpless" in cmds_cats[CategoryCmd.DEFAULT_CATEGORY]
88+
assert "has_help_func" in cmds_cats[CategoryCmd.DEFAULT_CATEGORY]
9789
assert "coding" in help_topics
9890

9991

@@ -116,8 +108,8 @@ class CategoryCommandSet(CommandSet):
116108

117109
DEFAULT_CATEGORY = "CategoryCommandSet Commands"
118110

119-
def do_documented(self, _: cmd2.Statement) -> None:
120-
"""This function has a docstring.
111+
def do_cmdset_command(self, _: cmd2.Statement) -> None:
112+
"""The cmdset command.
121113
122114
Since this class DOES define its own DEFAULT_CATEGORY,
123115
this command will show in CategoryCommandSet.DEFAULT_CATEGORY
@@ -127,12 +119,12 @@ def do_documented(self, _: cmd2.Statement) -> None:
127119
def test_no_category_command_set() -> None:
128120
app = cmd2.Cmd()
129121
app.register_command_set(NoCategoryCommandSet())
130-
cmds_cats, _cmds_undoc, _help_topics = app._build_command_info()
122+
cmds_cats, _help_topics = app._build_command_info()
131123
assert "inherit" in cmds_cats[CommandSet.DEFAULT_CATEGORY]
132124

133125

134126
def test_category_command_set() -> None:
135127
app = cmd2.Cmd()
136128
app.register_command_set(CategoryCommandSet())
137-
cmds_cats, _cmds_undoc, _help_topics = app._build_command_info()
138-
assert "documented" in cmds_cats[CategoryCommandSet.DEFAULT_CATEGORY]
129+
cmds_cats, _help_topics = app._build_command_info()
130+
assert "cmdset_command" in cmds_cats[CategoryCommandSet.DEFAULT_CATEGORY]

0 commit comments

Comments
 (0)