diff options
Diffstat (limited to 'cmd2')
-rw-r--r-- | cmd2/cmd2.py | 32 | ||||
-rw-r--r-- | cmd2/command_definition.py | 29 |
2 files changed, 35 insertions, 26 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index ef273d15..310ad32f 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -46,7 +46,7 @@ from typing import Any, AnyStr, Callable, Dict, Iterable, List, Mapping, Optiona from . import ansi, constants, plugin, utils from .argparse_custom import DEFAULT_ARGUMENT_PARSER, CompletionItem from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer -from .command_definition import _UNBOUND_COMMANDS, CommandSet, _partial_passthru +from .command_definition import _REGISTERED_COMMANDS, CommandSet, _partial_passthru from .constants import COMMAND_FUNC_PREFIX, COMPLETER_FUNC_PREFIX, HELP_FUNC_PREFIX from .decorators import with_argparser from .exceptions import Cmd2ShlexError, EmbeddedConsoleExit, EmptyStatement, RedirectionError, SkipPostcommandHooks @@ -403,8 +403,8 @@ class Cmd(cmd.Cmd): """ # start by loading registered functions as commands - for cmd_name, cmd_func, cmd_completer, cmd_help in _UNBOUND_COMMANDS: - self.install_command_function(cmd_name, cmd_func, cmd_completer, cmd_help) + for cmd_name in _REGISTERED_COMMANDS.keys(): + self.install_registered_command(cmd_name) # Search for all subclasses of CommandSet, instantiate them if they weren't provided in the constructor all_commandset_defs = CommandSet.__subclasses__() @@ -492,6 +492,28 @@ class Cmd(cmd.Cmd): cmdset.on_unregister(self) self._installed_command_sets.remove(cmdset) + def install_registered_command(self, cmd_name: str): + cmd_completer = None + cmd_help = None + + if cmd_name not in _REGISTERED_COMMANDS: + raise KeyError('Command ' + cmd_name + ' has not been registered') + + cmd_func = _REGISTERED_COMMANDS[cmd_name] + + module = inspect.getmodule(cmd_func) + + module_funcs = [mf for mf in inspect.getmembers(module) if inspect.isfunction(mf[1])] + for mf in module_funcs: + if mf[0] == COMPLETER_FUNC_PREFIX + cmd_name: + cmd_completer = mf[1] + elif mf[0] == HELP_FUNC_PREFIX + cmd_name: + cmd_help = mf[1] + if cmd_completer is not None and cmd_help is not None: + break + + self.install_command_function(cmd_name, cmd_func, cmd_completer, cmd_help) + def install_command_function(self, cmd_name: str, cmd_func: Callable, @@ -510,8 +532,8 @@ class Cmd(cmd.Cmd): if not valid: raise ValueError("Invalid command name {!r}: {}".format(cmd_name, errmsg)) - assert getattr(self, COMMAND_FUNC_PREFIX + cmd_name, None) is None,\ - 'Duplicate command function registered: ' + cmd_name + 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: diff --git a/cmd2/command_definition.py b/cmd2/command_definition.py index 115cef64..6996bd9d 100644 --- a/cmd2/command_definition.py +++ b/cmd2/command_definition.py @@ -5,17 +5,16 @@ Supports the definition of commands in separate classes to be composed into cmd2 import functools from typing import ( Callable, + Dict, Iterable, - List, Optional, - Tuple, Type, Union, ) -from .constants import COMMAND_FUNC_PREFIX, HELP_FUNC_PREFIX, COMPLETER_FUNC_PREFIX +from .constants import COMMAND_FUNC_PREFIX # Allows IDEs to resolve types without impacting imports at runtime, breaking circular dependency issues -try: +try: # pragma: no cover from typing import TYPE_CHECKING if TYPE_CHECKING: from .cmd2 import Cmd, Statement @@ -23,7 +22,7 @@ try: except ImportError: pass -_UNBOUND_COMMANDS = [] # type: List[Tuple[str, Callable, Optional[Callable], Optional[Callable]]] +_REGISTERED_COMMANDS = {} # type: Dict[str, Callable] """ Registered command tuples. (command, do_ function, complete_ function, help_ function """ @@ -69,24 +68,12 @@ def register_command(cmd_func: Callable[['Cmd', Union['Statement', 'argparse.Nam """ assert cmd_func.__name__.startswith(COMMAND_FUNC_PREFIX), 'Command functions must start with `do_`' - import inspect - cmd_name = cmd_func.__name__[len(COMMAND_FUNC_PREFIX):] - cmd_completer = None - cmd_help = None - - module = inspect.getmodule(cmd_func) - - module_funcs = [mf for mf in inspect.getmembers(module) if inspect.isfunction(mf[1])] - for mf in module_funcs: - if mf[0] == COMPLETER_FUNC_PREFIX + cmd_name: - cmd_completer = mf[1] - elif mf[0] == HELP_FUNC_PREFIX + cmd_name: - cmd_help = mf[1] - if cmd_completer is not None and cmd_help is not None: - break - _UNBOUND_COMMANDS.append((cmd_name, cmd_func, cmd_completer, cmd_help)) + if cmd_name not in _REGISTERED_COMMANDS: + _REGISTERED_COMMANDS[cmd_name] = cmd_func + else: + raise KeyError('Command ' + cmd_name + ' is already registered') return cmd_func |