@@ -617,10 +617,12 @@ def __init__(
617617
618618 # Commands disabled during specific application states
619619 # Key: Command name | Value: DisabledCommand object
620+ # NOTE: Use disable_command() and enable_command() to modify this dictionary.
620621 self .disabled_commands : dict [str , DisabledCommand ] = {}
621622
622623 # Categories of commands to be disabled
623624 # Key: Category name | Value: Message to display
625+ # NOTE: Use disable_category() and enable_category() to modify this dictionary.
624626 self .disabled_categories : dict [str , str ] = {}
625627
626628 # Command parsers for this Cmd instance.
@@ -1148,9 +1150,8 @@ def get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentPar
11481150 """Tokenize a command string and resolve the associated root parser and relative subcommand path.
11491151
11501152 This helper handles the initial resolution of a command string (e.g., 'foo bar baz') by
1151- identifying 'foo' as the root command (even if disabled), retrieving its associated
1152- parser, and returning any remaining tokens (['bar', 'baz']) as a path relative
1153- to that parser for further traversal.
1153+ identifying 'foo' as the root command, retrieving its associated parser, and returning
1154+ any remaining tokens (['bar', 'baz']) as a path relative to that parser for further traversal.
11541155
11551156 :param command: full space-delimited command path leading to a parser (e.g. 'foo' or 'foo bar')
11561157 :return: a tuple containing the Cmd2ArgumentParser for the root command and a list of
@@ -1166,11 +1167,7 @@ def get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentPar
11661167 subcommand_path = tokens [1 :]
11671168
11681169 # Search for the base command function and verify it has an argparser defined
1169- if root_command in self .disabled_commands :
1170- command_func = self .disabled_commands [root_command ].command_function
1171- else :
1172- command_func = self .get_command_func (root_command )
1173-
1170+ command_func = self .get_command_func (root_command )
11741171 if command_func is None :
11751172 raise ValueError (f"Root command '{ root_command } ' not found" )
11761173
@@ -4314,8 +4311,21 @@ def do_help(self, args: argparse.Namespace) -> None:
43144311
43154312 else :
43164313 # Getting help for a specific command
4317- func = self . get_command_func ( args .command )
4314+ disabled = args .command in self . disabled_commands
43184315 help_func = getattr (self , constants .HELP_FUNC_PREFIX + args .command , None )
4316+
4317+ # If the command is disabled, then call the help function which was
4318+ # overwritten by disable_command() to print the disabled message.
4319+ if disabled :
4320+ if help_func is not None :
4321+ help_func ()
4322+ else :
4323+ # This is a defensive fallback in case someone disabled a command
4324+ # without using disable_command() resulting in no help function.
4325+ self ._report_disabled_command_usage (message_to_print = f"{ args .command } is currently disabled." )
4326+ return
4327+
4328+ func = self .get_command_func (args .command )
43194329 argparser = None if func is None else self .command_parsers .get (func )
43204330
43214331 # If the command function uses argparse, then use argparse's help
@@ -5652,15 +5662,27 @@ def disable_command(self, command: str, message_to_print: str) -> None:
56525662 completer_function = getattr (self , completer_func_name , None ),
56535663 )
56545664
5655- # Overwrite the command and help functions to print the message
5656- new_func = functools .partial (
5657- self ._report_disabled_command_usage , message_to_print = message_to_print .replace (constants .COMMAND_NAME , command )
5658- )
5659- setattr (self , cmd_func_name , new_func )
5660- setattr (self , help_func_name , new_func )
5665+ # Overwrite command function to print the message
5666+ message_to_print = message_to_print .replace (constants .COMMAND_NAME , command )
5667+ new_cmd_func = functools .partial (self ._report_disabled_command_usage , message_to_print = message_to_print )
56615668
5662- # Set the completer to a function that returns a blank list
5663- setattr (self , completer_func_name , lambda * _args , ** _kwargs : [])
5669+ # Preserve the metadata of the original command function
5670+ functools .update_wrapper (new_cmd_func , command_function )
5671+ setattr (self , cmd_func_name , new_cmd_func )
5672+
5673+ # Overwrite the help function to print the message
5674+ new_help_func = functools .partial (self ._report_disabled_command_usage , message_to_print = message_to_print )
5675+ if (help_function := self .disabled_commands [command ].help_function ) is not None :
5676+ # Preserve the metadata of the original help function
5677+ functools .update_wrapper (new_help_func , help_function )
5678+ setattr (self , help_func_name , new_help_func )
5679+
5680+ # Set the completer to a function that returns a nothing
5681+ new_completer_func = functools .partial (self ._disabled_completer )
5682+ if (completer_function := self .disabled_commands [command ].completer_function ) is not None :
5683+ # Preserve the metadata of the original completer function
5684+ functools .update_wrapper (new_completer_func , completer_function )
5685+ setattr (self , completer_func_name , new_completer_func )
56645686
56655687 def disable_category (self , category : str , message_to_print : str ) -> None :
56665688 """Disable an entire category of commands.
@@ -5685,14 +5707,23 @@ def disable_category(self, category: str, message_to_print: str) -> None:
56855707 self .disabled_categories [category ] = message_to_print
56865708
56875709 def _report_disabled_command_usage (self , * _args : Any , message_to_print : str , ** _kwargs : Any ) -> None :
5688- """Report when a disabled command has been run or had help called on it .
5710+ """Report when a disabled command or its help function is run .
56895711
56905712 :param _args: not used
56915713 :param message_to_print: the message reporting that the command is disabled
56925714 :param _kwargs: not used
56935715 """
56945716 self .perror (message_to_print , style = None )
56955717
5718+ def _disabled_completer (self , * _args : Any , ** _kwargs : Any ) -> Completions :
5719+ """Completer function for a disabled command.
5720+
5721+ :param _args: not used
5722+ :param _kwargs: not used
5723+ :return: an empty Completions object
5724+ """
5725+ return Completions ()
5726+
56965727 def cmdloop (self , intro : RenderableType = "" ) -> int :
56975728 """Deal with extra features provided by cmd2, this is an outer wrapper around _cmdloop().
56985729
0 commit comments