diff options
author | Eric Lin <anselor@gmail.com> | 2020-06-16 14:41:25 -0400 |
---|---|---|
committer | anselor <anselor@gmail.com> | 2020-08-04 13:38:08 -0400 |
commit | 80dbdcbf96a247a08e7284674333330b61bb7ad3 (patch) | |
tree | 207ddaac75099ae1914121ba298f7b0b0002e3bb /cmd2 | |
parent | f995e7abdef961ec0813f749debb1cfef1f8989d (diff) | |
download | cmd2-git-80dbdcbf96a247a08e7284674333330b61bb7ad3.tar.gz |
Added more command validation. Moved some common behavior into private functions.
Diffstat (limited to 'cmd2')
-rw-r--r-- | cmd2/cmd2.py | 75 | ||||
-rw-r--r-- | cmd2/command_definition.py | 2 |
2 files changed, 50 insertions, 27 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 40748d51..25251bef 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -41,7 +41,7 @@ import types from code import InteractiveConsole from collections import namedtuple from contextlib import redirect_stdout -from typing import Any, AnyStr, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Type, Union +from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple, Type, Union from . import ansi, constants, plugin, utils from .argparse_custom import DEFAULT_ARGUMENT_PARSER, CompletionItem @@ -438,37 +438,69 @@ class Cmd(cmd.Cmd): try: for method in methods: command = method[0][len(COMMAND_FUNC_PREFIX):] - - valid, errmsg = self.statement_parser.is_valid_command(command) - if not valid: - raise ValueError("Invalid command name {!r}: {}".format(command, errmsg)) - - assert getattr(self, COMMAND_FUNC_PREFIX + command, None) is None, \ - 'In {}: Duplicate command function: {}'.format(type(cmdset).__name__, method[0]) - command_wrapper = _partial_passthru(method[1], self) - setattr(self, method[0], command_wrapper) + + self.__install_command_function(command, command_wrapper, type(cmdset).__name__) installed_attributes.append(method[0]) completer_func_name = COMPLETER_FUNC_PREFIX + command cmd_completer = getattr(cmdset, completer_func_name, None) - if cmd_completer and not getattr(self, completer_func_name, None): + if cmd_completer is not None: completer_wrapper = _partial_passthru(cmd_completer, self) - setattr(self, completer_func_name, completer_wrapper) + self.__install_completer_function(command, completer_wrapper) installed_attributes.append(completer_func_name) help_func_name = HELP_FUNC_PREFIX + command cmd_help = getattr(cmdset, help_func_name, None) - if cmd_help and not getattr(self, help_func_name, None): + if cmd_help is not None: help_wrapper = _partial_passthru(cmd_help, self) - setattr(self, help_func_name, help_wrapper) + self.__install_help_function(command, help_wrapper) installed_attributes.append(help_func_name) + self._installed_command_sets.append(cmdset) except Exception: for attrib in installed_attributes: delattr(self, attrib) raise + def __install_command_function(self, command, command_wrapper, context=''): + cmd_func_name = COMMAND_FUNC_PREFIX + command + + # Make sure command function doesn't share naem with existing attribute + if hasattr(self, cmd_func_name): + raise ValueError('Attribute already exists: {} ({})'.format(cmd_func_name, context)) + + # Check if command has an invalid name + valid, errmsg = self.statement_parser.is_valid_command(command) + if not valid: + raise ValueError("Invalid command name {!r}: {}".format(command, errmsg)) + + # Check if command shares a name with an alias + if command in self.aliases: + self.pwarning("Deleting alias '{}' because it shares its name with a new command".format(command)) + del self.aliases[command] + + # Check if command shares a name with a macro + if command in self.macros: + self.pwarning("Deleting macro '{}' because it shares its name with a new command".format(command)) + del self.macros[command] + + setattr(self, cmd_func_name, command_wrapper) + + def __install_completer_function(self, cmd_name, cmd_completer): + completer_func_name = COMPLETER_FUNC_PREFIX + cmd_name + + if hasattr(self, completer_func_name): + raise ValueError('Attribute already exists: {}'.format(completer_func_name)) + setattr(self, completer_func_name, cmd_completer) + + def __install_help_function(self, cmd_name, cmd_completer): + help_func_name = HELP_FUNC_PREFIX + cmd_name + + if hasattr(self, help_func_name): + raise ValueError('Attribute already exists: {}'.format(help_func_name)) + setattr(self, help_func_name, cmd_completer) + def uninstall_command_set(self, cmdset: CommandSet): """ Uninstalls a CommandSet and unloads all associated commands @@ -528,22 +560,13 @@ class Cmd(cmd.Cmd): :param cmd_help: help generator for the command :return: None """ - valid, errmsg = self.statement_parser.is_valid_command(cmd_name) - if not valid: - raise ValueError("Invalid command name {!r}: {}".format(cmd_name, errmsg)) + self.__install_command_function(cmd_name, types.MethodType(cmd_func, self)) - if getattr(self, COMMAND_FUNC_PREFIX + cmd_name, None) is not None: - raise KeyError('Duplicate command function registered: ' + cmd_name) - setattr(self, COMMAND_FUNC_PREFIX + cmd_name, types.MethodType(cmd_func, self)) self._installed_functions.append(cmd_name) if cmd_completer is not None: - assert getattr(self, COMPLETER_FUNC_PREFIX + cmd_name, None) is None, \ - 'Duplicate command completer registered: ' + COMPLETER_FUNC_PREFIX + cmd_name - setattr(self, COMPLETER_FUNC_PREFIX + cmd_name, types.MethodType(cmd_completer, self)) + self.__install_completer_function(cmd_name, types.MethodType(cmd_completer, self)) if cmd_help is not None: - assert getattr(self, HELP_FUNC_PREFIX + cmd_name, None) is None, \ - 'Duplicate command help registered: ' + HELP_FUNC_PREFIX + cmd_name - setattr(self, HELP_FUNC_PREFIX + cmd_name, types.MethodType(cmd_help, self)) + self.__install_help_function(cmd_name, types.MethodType(cmd_help, self)) def uninstall_command(self, cmd_name: str): """ diff --git a/cmd2/command_definition.py b/cmd2/command_definition.py index 1768d86e..b5c9fbca 100644 --- a/cmd2/command_definition.py +++ b/cmd2/command_definition.py @@ -19,7 +19,7 @@ try: # pragma: no cover if TYPE_CHECKING: from .cmd2 import Cmd, Statement import argparse -except ImportError: +except ImportError: # pragma: no cover pass _REGISTERED_COMMANDS = {} # type: Dict[str, Callable] |