summaryrefslogtreecommitdiff
path: root/cmd2
diff options
context:
space:
mode:
authorEric Lin <anselor@gmail.com>2020-08-12 13:08:59 -0400
committeranselor <anselor@gmail.com>2020-08-12 17:41:20 -0400
commit774fb39d7e259d0679c573b0d893293f9ed9aed9 (patch)
treea78a4693e7cca707668eb89b0d8e41c3fedd108e /cmd2
parent4d628ea7573ef9016971dbbf7de9126c6d179227 (diff)
downloadcmd2-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.py3
-rw-r--r--cmd2/argparse_custom.py11
-rw-r--r--cmd2/cmd2.py31
-rw-r--r--cmd2/command_definition.py35
-rw-r--r--cmd2/decorators.py14
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))