diff options
author | Eric Lin <anselor@gmail.com> | 2020-08-12 13:08:59 -0400 |
---|---|---|
committer | anselor <anselor@gmail.com> | 2020-08-12 17:41:20 -0400 |
commit | 774fb39d7e259d0679c573b0d893293f9ed9aed9 (patch) | |
tree | a78a4693e7cca707668eb89b0d8e41c3fedd108e /cmd2 | |
parent | 4d628ea7573ef9016971dbbf7de9126c6d179227 (diff) | |
download | cmd2-git-774fb39d7e259d0679c573b0d893293f9ed9aed9.tar.gz |
Breaking change: Removed cmd2 app as a required second parameter to
CommandSet command functions (do_, complete_, help_).
Renamed install_command_set and uninstall_command_set to
register_command_set and unregister_command_set.
Diffstat (limited to 'cmd2')
-rw-r--r-- | cmd2/argparse_completer.py | 3 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 11 | ||||
-rw-r--r-- | cmd2/cmd2.py | 31 | ||||
-rw-r--r-- | cmd2/command_definition.py | 35 | ||||
-rw-r--r-- | cmd2/decorators.py | 14 |
5 files changed, 26 insertions, 68 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index f14e83fd..77fa41b8 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -606,7 +606,8 @@ class ArgparseCompleter: # No cases matched, raise an error raise CompletionError('Could not find CommandSet instance matching defining type for completer') args.append(cmd_set) - args.append(self._cmd2_app) + else: + args.append(self._cmd2_app) # Check if arg_choices.to_call expects arg_tokens to_call_params = inspect.signature(arg_choices.to_call).parameters diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 5dbb9f66..d724cb88 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -68,9 +68,9 @@ If bound to a cmd2.Cmd subclass, it will pass the app instance as the `self` argument. This is good in cases where the list of choices being generated relies on state data of the cmd2-based app. If bound to a cmd2.CommandSet subclass, it will pass the CommandSet instance -as the `self` argument, and the app instance as the positional argument. +as the `self` argument. - Example bound to cmd2.Cmd:: + Example:: def my_choices_method(self): ... @@ -78,13 +78,6 @@ as the `self` argument, and the app instance as the positional argument. parser.add_argument("arg", choices_method=my_choices_method) - Example bound to cmd2.CommandSEt:: - - def my_choices_method(self, app: cmd2.Cmd): - ... - return my_generated_list - - parser.add_argument("arg", choices_method=my_choices_method) ``completer_function`` - pass a tab completion function that does custom completion. Since custom tab completion operations commonly need to modify diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 621228db..30dcb6e8 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -45,7 +45,7 @@ from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Tuple 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 CommandSet, _partial_passthru +from .command_definition import CommandSet from .constants import COMMAND_FUNC_PREFIX, COMPLETER_FUNC_PREFIX, HELP_FUNC_PREFIX from .decorators import with_argparser, as_subcommand_to from .exceptions import ( @@ -186,7 +186,7 @@ class Cmd(cmd.Cmd): :param auto_load_commands: If True, cmd2 will check for all subclasses of `CommandSet` that are currently loaded by Python and automatically instantiate and register all commands. If False, CommandSets - must be manually installed with `install_command_set`. + must be manually installed with `register_command_set`. """ # If use_ipython is False, make sure the ipy command isn't available in this instance if not use_ipython: @@ -264,7 +264,7 @@ class Cmd(cmd.Cmd): self._cmd_to_command_sets = {} # type: Dict[str, CommandSet] if command_sets: for command_set in command_sets: - self.install_command_set(command_set) + self.register_command_set(command_set) if auto_load_commands: self._autoload_commands() @@ -452,11 +452,11 @@ class Cmd(cmd.Cmd): or len(init_sig.parameters) != 1 or 'self' not in init_sig.parameters): cmdset = cmdset_type() - self.install_command_set(cmdset) + self.register_command_set(cmdset) load_commandset_by_type(all_commandset_defs) - def install_command_set(self, cmdset: CommandSet) -> None: + def register_command_set(self, cmdset: CommandSet) -> None: """ Installs a CommandSet, loading all commands defined in the CommandSet @@ -476,23 +476,20 @@ class Cmd(cmd.Cmd): try: for method_name, method in methods: command = method_name[len(COMMAND_FUNC_PREFIX):] - command_wrapper = _partial_passthru(method, self) - self._install_command_function(command, command_wrapper, type(cmdset).__name__) + self._install_command_function(command, method, type(cmdset).__name__) installed_attributes.append(method_name) completer_func_name = COMPLETER_FUNC_PREFIX + command cmd_completer = getattr(cmdset, completer_func_name, None) if cmd_completer is not None: - completer_wrapper = _partial_passthru(cmd_completer, self) - self._install_completer_function(command, completer_wrapper) + self._install_completer_function(command, cmd_completer) installed_attributes.append(completer_func_name) help_func_name = HELP_FUNC_PREFIX + command cmd_help = getattr(cmdset, help_func_name, None) if cmd_help is not None: - help_wrapper = _partial_passthru(cmd_help, self) - self._install_help_function(command, help_wrapper) + self._install_help_function(command, cmd_help) installed_attributes.append(help_func_name) self._cmd_to_command_sets[command] = cmdset @@ -508,7 +505,7 @@ class Cmd(cmd.Cmd): if cmdset in self._cmd_to_command_sets.values(): self._cmd_to_command_sets = \ {key: val for key, val in self._cmd_to_command_sets.items() if val is not cmdset} - cmdset.on_unregister(self) + cmdset.on_unregister() raise def _install_command_function(self, command: str, command_wrapper: Callable, context=''): @@ -549,7 +546,7 @@ class Cmd(cmd.Cmd): raise CommandSetRegistrationError('Attribute already exists: {}'.format(help_func_name)) setattr(self, help_func_name, cmd_help) - def uninstall_command_set(self, cmdset: CommandSet): + def unregister_command_set(self, cmdset: CommandSet): """ Uninstalls a CommandSet and unloads all associated commands :param cmdset: CommandSet to uninstall @@ -581,7 +578,7 @@ class Cmd(cmd.Cmd): if hasattr(self, HELP_FUNC_PREFIX + cmd_name): delattr(self, HELP_FUNC_PREFIX + cmd_name) - cmdset.on_unregister(self) + cmdset.on_unregister() self._installed_command_sets.remove(cmdset) def _check_uninstallable(self, cmdset: CommandSet): @@ -656,11 +653,7 @@ class Cmd(cmd.Cmd): raise CommandSetRegistrationError('Could not find argparser for command "{}" needed by subcommand: {}' .format(command_name, str(method))) - if isinstance(cmdset, CommandSet): - command_handler = _partial_passthru(method, self) - else: - command_handler = method - subcmd_parser.set_defaults(cmd2_handler=command_handler) + subcmd_parser.set_defaults(cmd2_handler=method) def find_subcommand(action: argparse.ArgumentParser, subcmd_names: List[str]) -> argparse.ArgumentParser: if not subcmd_names: diff --git a/cmd2/command_definition.py b/cmd2/command_definition.py index 22d8915e..86f1151a 100644 --- a/cmd2/command_definition.py +++ b/cmd2/command_definition.py @@ -18,36 +18,6 @@ except ImportError: # pragma: no cover pass -def _partial_passthru(func: Callable, *args, **kwargs) -> functools.partial: - """ - Constructs a partial function that passes arguments through to the wrapped function. - Must construct a new type every time so that each wrapped function's __doc__ can be copied correctly. - - :param func: wrapped function - :param args: positional arguments - :param kwargs: keyword arguments - :return: partial function that exposes attributes of wrapped function - """ - def __getattr__(self, item): - return getattr(self.func, item) - - def __setattr__(self, key, value): - return setattr(self.func, key, value) - - def __dir__(self) -> Iterable[str]: - return dir(self.func) - - passthru_type = type('PassthruPartial' + func.__name__, - (functools.partial,), - { - '__getattr__': __getattr__, - '__setattr__': __setattr__, - '__dir__': __dir__, - }) - passthru_type.__doc__ = func.__doc__ - return passthru_type(func, *args, **kwargs) - - def with_default_category(category: str): """ Decorator that applies a category to all ``do_*`` command methods in a class that do not already @@ -78,8 +48,7 @@ class CommandSet(object): ``with_default_category`` can be used to apply a default category to all commands in the CommandSet. - ``do_``, ``help_``, and ``complete_`` functions differ only in that they're now required to accept - a reference to ``cmd2.Cmd`` as the first argument after self. + ``do_``, ``help_``, and ``complete_`` functions differ only in that self is the CommandSet instead of the cmd2 app """ def __init__(self): @@ -98,7 +67,7 @@ class CommandSet(object): else: raise CommandSetRegistrationError('This CommandSet has already been registered') - def on_unregister(self, cmd): + def on_unregister(self): """ Called by ``cmd2.Cmd`` when a CommandSet is unregistered and removed. diff --git a/cmd2/decorators.py b/cmd2/decorators.py index 0dda3485..7c20af68 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -41,7 +41,7 @@ def with_category(category: str) -> Callable: ########################## -def _parse_positionals(args: Tuple) -> Tuple['cmd2.Cmd', Union[Statement, str]]: +def _parse_positionals(args: Tuple) -> Tuple[Union['cmd2.Cmd', 'cmd2.CommandSet'], Union[Statement, str]]: """ Helper function for cmd2 decorators to inspect the positional arguments until the cmd2.Cmd argument is found Assumes that we will find cmd2.Cmd followed by the command statement object or string. @@ -49,8 +49,10 @@ def _parse_positionals(args: Tuple) -> Tuple['cmd2.Cmd', Union[Statement, str]]: :return: The cmd2.Cmd reference and the command line statement """ for pos, arg in enumerate(args): - from cmd2 import Cmd - if isinstance(arg, Cmd) and len(args) > pos: + from cmd2 import Cmd, CommandSet + if (isinstance(arg, Cmd) or isinstance(arg, CommandSet)) and len(args) > pos: + if isinstance(arg, CommandSet): + arg = arg._cmd next_arg = args[pos + 1] if isinstance(next_arg, (Statement, str)): return arg, args[pos + 1] @@ -92,7 +94,7 @@ def with_argument_list(*args: List[Callable], preserve_quotes: bool = False) -> >>> def do_echo(self, arglist): >>> self.poutput(' '.join(arglist) """ - import functools + import functools, cmd2 def arg_decorator(func: Callable): @functools.wraps(func) @@ -293,8 +295,8 @@ def with_argparser(parser: argparse.ArgumentParser, *, else: setattr(ns, '__statement__', statement) - def get_handler(self: argparse.Namespace) -> Optional[Callable]: - return getattr(self, constants.SUBCMD_HANDLER, None) + def get_handler(ns_self: argparse.Namespace) -> Optional[Callable]: + return getattr(ns_self, constants.SUBCMD_HANDLER, None) setattr(ns, 'get_handler', types.MethodType(get_handler, ns)) |