Skip to content

Commit 48bfc0d

Browse files
authored
Fixed issue where CommandSet registration did not respect disabled categories (#1568)
1 parent 371205b commit 48bfc0d

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Bug Fixes
44
- Fixed issue where `delimiter_complete()` could cause more matches than display matches
5+
- Fixed issue where `CommandSet` registration did not respect disabled categories
56

67
## 3.1.2 (January 26, 2026)
78

cmd2/cmd2.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,14 @@ def __init__(
593593
# being printed by a command.
594594
self.terminal_lock = threading.RLock()
595595

596-
# Commands that have been disabled from use. This is to support commands that are only available
597-
# during specific states of the application. This dictionary's keys are the command names and its
598-
# values are DisabledCommand objects.
596+
# Commands disabled during specific application states
597+
# Key: Command name | Value: DisabledCommand object
599598
self.disabled_commands: dict[str, DisabledCommand] = {}
600599

600+
# Categories of commands to be disabled
601+
# Key: Category name | Value: Message to display
602+
self.disabled_categories: dict[str, str] = {}
603+
601604
# The default key for sorting string results. Its default value performs a case-insensitive alphabetical sort.
602605
# If natural sorting is preferred, then set this to NATURAL_SORT_KEY.
603606
# cmd2 uses this key for sorting:
@@ -788,6 +791,12 @@ def register_command_set(self, cmdset: CommandSet) -> None:
788791
if default_category and not hasattr(command_method, constants.CMD_ATTR_HELP_CATEGORY):
789792
utils.categorize(command_method, default_category)
790793

794+
# If this command is in a disabled category, then disable it
795+
command_category = getattr(command_method, constants.CMD_ATTR_HELP_CATEGORY, None)
796+
if command_category in self.disabled_categories:
797+
message_to_print = self.disabled_categories[command_category]
798+
self.disable_command(command, message_to_print)
799+
791800
self._installed_command_sets.add(cmdset)
792801

793802
self._register_subcommands(cmdset)
@@ -5819,7 +5828,7 @@ def enable_command(self, command: str) -> None:
58195828
58205829
:param command: the command being enabled
58215830
"""
5822-
# If the commands is already enabled, then return
5831+
# If the command is already enabled, then return
58235832
if command not in self.disabled_commands:
58245833
return
58255834

@@ -5851,11 +5860,17 @@ def enable_category(self, category: str) -> None:
58515860
58525861
:param category: the category to enable
58535862
"""
5863+
# If the category is already enabled, then return
5864+
if category not in self.disabled_categories:
5865+
return
5866+
58545867
for cmd_name in list(self.disabled_commands):
58555868
func = self.disabled_commands[cmd_name].command_function
58565869
if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
58575870
self.enable_command(cmd_name)
58585871

5872+
del self.disabled_categories[category]
5873+
58595874
def disable_command(self, command: str, message_to_print: str) -> None:
58605875
"""Disable a command and overwrite its functions.
58615876
@@ -5866,7 +5881,7 @@ def disable_command(self, command: str, message_to_print: str) -> None:
58665881
command being disabled.
58675882
ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"
58685883
"""
5869-
# If the commands is already disabled, then return
5884+
# If the command is already disabled, then return
58705885
if command in self.disabled_commands:
58715886
return
58725887

@@ -5905,13 +5920,19 @@ def disable_category(self, category: str, message_to_print: str) -> None:
59055920
of the command being disabled.
59065921
ex: message_to_print = f"{cmd2.COMMAND_NAME} is currently disabled"
59075922
"""
5923+
# If the category is already disabled, then return
5924+
if category in self.disabled_categories:
5925+
return
5926+
59085927
all_commands = self.get_all_commands()
59095928

59105929
for cmd_name in all_commands:
59115930
func = self.cmd_func(cmd_name)
59125931
if getattr(func, constants.CMD_ATTR_HELP_CATEGORY, None) == category:
59135932
self.disable_command(cmd_name, message_to_print)
59145933

5934+
self.disabled_categories[category] = message_to_print
5935+
59155936
def _report_disabled_command_usage(self, *_args: Any, message_to_print: str, **_kwargs: Any) -> None:
59165937
"""Report when a disabled command has been run or had help called on it.
59175938

tests/test_cmd2.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
COMMAND_NAME,
2121
Cmd2Style,
2222
Color,
23+
CommandSet,
2324
RichPrintKwargs,
2425
clipboard,
2526
constants,
@@ -3106,6 +3107,16 @@ def do_has_no_helper_funcs(self, arg) -> None:
31063107
self.poutput("The real has_no_helper_funcs")
31073108

31083109

3110+
class DisableCommandSet(CommandSet):
3111+
"""Test registering a command which is in a disabled category"""
3112+
3113+
category_name = "CommandSet Test Category"
3114+
3115+
@cmd2.with_category(category_name)
3116+
def do_new_command(self, arg) -> None:
3117+
self._cmd.poutput("CommandSet function is enabled")
3118+
3119+
31093120
@pytest.fixture
31103121
def disable_commands_app():
31113122
return DisableCommandsApp()
@@ -3209,7 +3220,7 @@ def test_enable_enabled_command(disable_commands_app) -> None:
32093220
saved_len = len(disable_commands_app.disabled_commands)
32103221
disable_commands_app.enable_command('has_helper_funcs')
32113222

3212-
# The number of disabled_commands should not have changed
3223+
# The number of disabled commands should not have changed
32133224
assert saved_len == len(disable_commands_app.disabled_commands)
32143225

32153226

@@ -3223,7 +3234,7 @@ def test_disable_command_twice(disable_commands_app) -> None:
32233234
message_to_print = 'These commands are currently disabled'
32243235
disable_commands_app.disable_command('has_helper_funcs', message_to_print)
32253236

3226-
# The length of disabled_commands should have increased one
3237+
# The number of disabled commands should have increased one
32273238
new_len = len(disable_commands_app.disabled_commands)
32283239
assert saved_len == new_len - 1
32293240
saved_len = new_len
@@ -3251,6 +3262,50 @@ def test_disabled_message_command_name(disable_commands_app) -> None:
32513262
assert err[0].startswith('has_helper_funcs is currently disabled')
32523263

32533264

3265+
def test_register_command_in_enabled_category(disable_commands_app) -> None:
3266+
disable_commands_app.enable_category(DisableCommandSet.category_name)
3267+
cs = DisableCommandSet()
3268+
disable_commands_app.register_command_set(cs)
3269+
3270+
out, _err = run_cmd(disable_commands_app, 'new_command')
3271+
assert out[0] == "CommandSet function is enabled"
3272+
3273+
3274+
def test_register_command_in_disabled_category(disable_commands_app) -> None:
3275+
message_to_print = "CommandSet function is disabled"
3276+
disable_commands_app.disable_category(DisableCommandSet.category_name, message_to_print)
3277+
cs = DisableCommandSet()
3278+
disable_commands_app.register_command_set(cs)
3279+
3280+
_out, err = run_cmd(disable_commands_app, 'new_command')
3281+
assert err[0] == message_to_print
3282+
3283+
3284+
def test_enable_enabled_category(disable_commands_app) -> None:
3285+
# Test enabling a category that is not disabled
3286+
saved_len = len(disable_commands_app.disabled_categories)
3287+
disable_commands_app.enable_category('Test Category')
3288+
3289+
# The number of disabled categories should not have changed
3290+
assert saved_len == len(disable_commands_app.disabled_categories)
3291+
3292+
3293+
def test_disable_category_twice(disable_commands_app) -> None:
3294+
saved_len = len(disable_commands_app.disabled_categories)
3295+
message_to_print = 'These commands are currently disabled'
3296+
disable_commands_app.disable_category('Test Category', message_to_print)
3297+
3298+
# The number of disabled categories should have increased one
3299+
new_len = len(disable_commands_app.disabled_categories)
3300+
assert saved_len == new_len - 1
3301+
saved_len = new_len
3302+
3303+
# Disable again and the length should not change
3304+
disable_commands_app.disable_category('Test Category', message_to_print)
3305+
new_len = len(disable_commands_app.disabled_categories)
3306+
assert saved_len == new_len
3307+
3308+
32543309
@pytest.mark.parametrize('silence_startup_script', [True, False])
32553310
def test_startup_script(request, capsys, silence_startup_script) -> None:
32563311
test_dir = os.path.dirname(request.module.__file__)

0 commit comments

Comments
 (0)