@@ -279,6 +279,14 @@ class EmptyStatement(Exception):
279279 pass
280280
281281
282+ class DisabledCommand :
283+ """Contains data about a disabled command"""
284+ def __init__ (self ):
285+ # These are used to restore the original functions when the command is enabled
286+ self .command_function = None
287+ self .help_function = None
288+
289+
282290class Cmd (cmd .Cmd ):
283291 """An easy but powerful framework for writing line-oriented command interpreters.
284292
@@ -521,6 +529,11 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
521529 # being printed by a command.
522530 self .terminal_lock = threading .RLock ()
523531
532+ # Commands that have been disabled from use. This is to support commands that are only available
533+ # during specific states of the application. This dictionary's keys are the command names and its
534+ # values are DisabledCommand objects.
535+ self .disabled_commands = dict ()
536+
524537 # ----- Methods related to presenting output to the user -----
525538
526539 @property
@@ -1562,14 +1575,19 @@ def get_all_commands(self) -> List[str]:
15621575 if name .startswith (COMMAND_FUNC_PREFIX ) and callable (getattr (self , name ))]
15631576
15641577 def get_visible_commands (self ) -> List [str ]:
1565- """Returns a list of commands that have not been hidden."""
1578+ """Returns a list of commands that have not been hidden or disabled ."""
15661579 commands = self .get_all_commands ()
15671580
15681581 # Remove the hidden commands
15691582 for name in self .hidden_commands :
15701583 if name in commands :
15711584 commands .remove (name )
15721585
1586+ # Remove the disabled commands
1587+ for name in self .disabled_commands :
1588+ if name in commands :
1589+ commands .remove (name )
1590+
15731591 return commands
15741592
15751593 def get_alias_names (self ) -> List [str ]:
@@ -1953,7 +1971,7 @@ def cmd_func_name(self, command: str) -> str:
19531971 def onecmd (self , statement : Union [Statement , str ]) -> bool :
19541972 """ This executes the actual do_* method for a command.
19551973
1956- If the command provided doesn't exist, then it executes _default () instead.
1974+ If the command provided doesn't exist, then it executes default () instead.
19571975
19581976 :param statement: intended to be a Statement instance parsed command from the input stream, alternative
19591977 acceptance of a str is present only for backward compatibility with cmd
@@ -3186,13 +3204,15 @@ def do_history(self, args: argparse.Namespace) -> None:
31863204
31873205 # -v must be used alone with no other options
31883206 if args .verbose :
3189- if args .clear or args .edit or args .output_file or args .run or args .transcript or args .expanded or args .script :
3207+ if (args .clear or args .edit or args .output_file or args .run or
3208+ args .transcript or args .expanded or args .script ):
31903209 self .poutput ("-v can not be used with any other options" )
31913210 self .poutput (self .history_parser .format_usage ())
31923211 return
31933212
31943213 # -s and -x can only be used if none of these options are present: [-c -r -e -o -t]
3195- if (args .script or args .expanded ) and (args .clear or args .edit or args .output_file or args .run or args .transcript ):
3214+ if (args .script or args .expanded ) and (args .clear or args .edit or args .output_file or args .run or
3215+ args .transcript ):
31963216 self .poutput ("-s and -x can not be used with -c, -r, -e, -o, or -t" )
31973217 self .poutput (self .history_parser .format_usage ())
31983218 return
@@ -3598,6 +3618,85 @@ def set_window_title(self, title: str) -> None: # pragma: no cover
35983618 else :
35993619 raise RuntimeError ("another thread holds terminal_lock" )
36003620
3621+ def enable_command (self , command : str ) -> None :
3622+ """
3623+ Enable a command by restoring its functions
3624+ :param command: the command being enabled
3625+ """
3626+ # If the commands is already enabled, then return
3627+ if command not in self .disabled_commands :
3628+ return
3629+
3630+ help_func_name = HELP_FUNC_PREFIX + command
3631+
3632+ # Restore the command and help functions to their original values
3633+ dc = self .disabled_commands [command ]
3634+ setattr (self , self .cmd_func_name (command ), dc .command_function )
3635+
3636+ if dc .help_function is None :
3637+ delattr (self , help_func_name )
3638+ else :
3639+ setattr (self , help_func_name , dc .help_function )
3640+
3641+ # Remove the disabled command entry
3642+ del self .disabled_commands [command ]
3643+
3644+ def enable_category (self , category : str ) -> None :
3645+ """
3646+ Enable an entire category of commands
3647+ :param category: the category to enable
3648+ """
3649+ for cmd_name in self .disabled_commands :
3650+ dc = self .disabled_commands [cmd_name ]
3651+ cmd_category = getattr (dc .command_function , HELP_CATEGORY , None )
3652+ if cmd_category is not None and cmd_category == category :
3653+ self .enable_command (cmd_name )
3654+
3655+ def disable_command (self , command : str , message_to_print : str ) -> None :
3656+ """
3657+ Disable a command and overwrite its functions
3658+ :param command: the command being disabled
3659+ :param message_to_print: what to print when this command or its help function is run while disabled
3660+ """
3661+ import functools
3662+
3663+ # If the commands is already disabled, then return
3664+ if command in self .disabled_commands :
3665+ return
3666+
3667+ # Make sure this is an actual command
3668+ command_function = self .cmd_func (command )
3669+ if command_function is None :
3670+ raise AttributeError ("{} does not refer to a command" .format (command ))
3671+
3672+ help_func_name = HELP_FUNC_PREFIX + command
3673+
3674+ # Add the disabled command record
3675+ dc = DisabledCommand ()
3676+ dc .command_function = command_function
3677+ dc .help_function = getattr (self , help_func_name , None )
3678+ self .disabled_commands [command ] = dc
3679+
3680+ # Overwrite the command and help functions to print the message
3681+ setattr (self , self .cmd_func_name (command ), functools .partial (self .poutput , message_to_print + '\n ' ))
3682+ setattr (self , help_func_name , functools .partial (self .poutput , message_to_print + '\n ' ))
3683+
3684+ def disable_category (self , category : str , message_to_print : str ) -> None :
3685+ """
3686+ Disable an entire category of commands
3687+ :param category: the category to disable
3688+ :param message_to_print: what to print when anything in this category is run while disabled
3689+ """
3690+ all_commands = self .get_all_commands ()
3691+
3692+ for cmd_name in all_commands :
3693+ func = self .cmd_func (cmd_name )
3694+ cmd_category = getattr (func , HELP_CATEGORY , None )
3695+
3696+ # If this command is in the category, then disable it
3697+ if cmd_category is not None and cmd_category == category :
3698+ self .disable_command (cmd_name , message_to_print )
3699+
36013700 def cmdloop (self , intro : Optional [str ] = None ) -> None :
36023701 """This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.
36033702
0 commit comments