Skip to content

Commit 4c03d88

Browse files
committed
Added way to disable commands
1 parent d6c6cf3 commit 4c03d88

File tree

1 file changed

+103
-4
lines changed

1 file changed

+103
-4
lines changed

cmd2/cmd2.py

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
282290
class 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

Comments
 (0)